hdu4670(树上点分治+状态压缩)

树上路径的f(u,v)=路径上所有点的乘积。

树上每个点的权值都是由给定的k个素数组合而成的,如果f(u,v)是立方数,那么就说明f(u,v)是可行的方案。

问有多少种可行的方案。

f(u,v)可是用状态压缩来表示,因为最多只有30个素数, 第i位表示第i个素数的幂,那么每一位的状态只有0,1,2因为3和0是等价的,所以用3进制状态来表示就行了。

其他代码就是裸的树分。

另外要注意的是,因为counts函数没有统计只有一个点的情况,所以需要另外统计。

  1 #pragma warning(disable:4996)
  2 #pragma comment(linker, "/STACK:1024000000,1024000000")
  3 #include <stdio.h>
  4 #include <string.h>
  5 #include <time.h>
  6 #include <math.h>
  7 #include <map>
  8 #include <set>
  9 #include <queue>
 10 #include <stack>
 11 #include <vector>
 12 #include <bitset>
 13 #include <algorithm>
 14 #include <iostream>
 15 #include <string>
 16 #include <functional>
 17 #include <unordered_map>
 18 const int INF = 1 << 30;
 19 typedef __int64 LL;
 20 /*
 21 用三进制的每一位表示第i个素数的幂
 22 如果幂都是0,那么说明是立方
 23 */
 24 const int N = 50000 + 10;
 25 std::vector<int> g[N];
 26 std::unordered_map<LL, int> mp;
 27 struct Node
 28 {
 29     int sta[33];
 30 }node[N];
 31 LL prime[33];
 32 std::vector<Node> dist;
 33 int n, k;
 34 int size[N], vis[N], total, root, mins;
 35 LL _3bit[33];
 36 void init()
 37 {
 38     _3bit[0] = 1;
 39     for (int i = 1;i <= 32;++i)
 40         _3bit[i] = _3bit[i - 1] * 3;
 41 }
 42 void getRoot(int u, int fa)
 43 {
 44     int maxs = 0;
 45     size[u] = 1;
 46     for (int i = 0;i < g[u].size();++i)
 47     {
 48         int v = g[u][i];
 49         if (v == fa || vis[v]) continue;
 50         getRoot(v, u);
 51         size[u] += size[v];
 52         maxs = std::max(maxs, size[v]);
 53     }
 54     maxs = std::max(maxs, total - size[u]);
 55     if (mins > maxs)
 56     {
 57         mins = maxs;
 58         root = u;
 59     }
 60 }
 61 void getDis(int u, int fa, Node d)
 62 {
 63     dist.push_back(d);
 64     for (int i = 0;i < g[u].size();++i)
 65     {
 66         int v = g[u][i];
 67         if (v == fa || vis[v]) continue;
 68         Node tmp;
 69         for (int j = 0;j < k;++j)
 70             tmp.sta[j] = (d.sta[j] + node[v].sta[j]) % 3;
 71         getDis(v, u, tmp);
 72     }
 73 }
 74 LL counts(int u)//计算经过u点的路径
 75 {
 76     mp.clear();
 77     mp[0] = 1;
 78     LL ret = 0;
 79     for (int i = 0;i < g[u].size();++i)
 80     {
 81         int v = g[u][i];
 82         if (vis[v]) continue;
 83         dist.clear();
 84         getDis(v, u, node[v]);
 85         for (int j = 0;j < dist.size();++j)
 86         {
 87             LL sta = 0;
 88             for (int z = 0;z < k;++z)
 89             {
 90                 sta += (3 - (node[u].sta[z] + dist[j].sta[z]) % 3) % 3 * _3bit[z];
 91             }
 92             ret += mp[sta];
 93         }
 94         for (int j = 0;j < dist.size();++j)
 95         {
 96             LL sta = 0;
 97             for (int z = 0;z < k;++z)
 98                 sta += dist[j].sta[z] * _3bit[z];
 99             mp[sta]++;
100         }
101     }
102     return ret;
103 }
104 LL ans;
105 void go(int u)
106 {
107     vis[u] = true;
108     ans += counts(u);
109     for (int i = 0;i < g[u].size(); ++i)
110     {
111         int v = g[u][i];
112         if (vis[v]) continue;
113         total = size[v];
114         mins = INF;
115         getRoot(v, u);
116         go(root);
117     }
118
119 }
120 int main()
121 {
122     int u, v;
123     LL x;
124     init();
125     while (scanf("%d%d", &n, &k) != EOF)
126     {
127         for (int i = 0;i < k;++i)
128             scanf("%I64d", &prime[i]);
129         ans = 0;
130         for (int i = 1;i <= n;++i)
131         {
132             g[i].clear();
133             vis[i] = 0;
134             scanf("%I64d", &x);
135             memset(node[i].sta, 0, sizeof(node[i].sta));
136             int tmp = 0;
137             for (int j = 0;j <k;++j)
138             {
139
140                 while (x%prime[j] == 0 && x)
141                 {
142                     node[i].sta[j]++;
143                     x /= prime[j];
144                 }
145                 node[i].sta[j] %= 3;
146                 if (node[i].sta[j] != 0)tmp++;
147             }
148             if (tmp == 0)//统计只有一个点的
149                 ans++;
150         }
151         for (int i = 1;i < n;++i)
152         {
153             scanf("%d%d", &u, &v);
154             g[u].push_back(v);
155             g[v].push_back(u);
156         }
157         total = n;
158         mins = INF;
159         getRoot(1, -1);
160         go(root);
161         printf("%I64d\n", ans);
162     }
163     return 0;
164 }

时间: 2024-10-01 07:39:13

hdu4670(树上点分治+状态压缩)的相关文章

POJ 3691 (AC自动机+状态压缩DP)

题目链接:  http://poj.org/problem?id=3691 题目大意:给定N的致病DNA片段以及一个最终DNA片段.问最终DNA片段最少修改多少个字符,使得不包含任一致病DNA. 解题思路: 首先说一下AC自动机在本题中的作用. ①字典树部分:负责判断当前0~i个字符组成的串是否包含致病DNA,这部分靠字典树上的cnt标记完成. ②匹配部分:主要依赖于匹配和失配转移关系的计算,这部分非常重要,用来构建不同字符间状态压缩的转移关系(代替反人类的位运算). 这也是必须使用AC自动机而

topcoder-srm701-div2-900 博弈\计算二进制位1的个数\dp\状态压缩

借用一下qls翻译过来的题面 现在有 n 个石子,A 和 B 轮流取石子,A先,每次最多可以取 m 个石子,取到最后一个石子的人获胜,但是某个人如果取完石子时候剩余石子数的二进制表示中有奇数个1,这个人就输了给定 n 和 m,问谁赢n<=5e8, m<=50TL 2s 以前我是从来没接触过博弈的 首先普及一下博弈的基本知识.. 必胜态,必败态,以及必胜点与必败点 首先有一个字必须要看清楚,那就是"必"字 是必胜而不是,胜利就行,这个字很关键 如图所示,一个点是p-posit

胜利大逃亡(续)(状态压缩bfs)

胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 7357    Accepted Submission(s): 2552 Problem Description Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)……这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带

uva 818(dfs+图+状态压缩)

题意:有n个环,编号从1到n,给出了一些环环相扣的情况,比如给a和b表示a和b两个环的扣在一起的,每个环都是可以打开的,问最少打开多少个环,然后再扣好,可以让所有的环成为一条链. 题解:状态压缩把所有的打开环的情况枚举出来,然后拿去判断是否成立,更新打开环后的图g[i][j],和每个点的度数,不成立有三种情况,1.计算没有打开的环的度数,如果大于2说明不会有链,2.把没有打开环拿去dfs,访问过就vis[i]++,如果vis[i]>=2说明存在环,3.如果打开的环数num + 1小于链的数量,说

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反

uva 11195 Another queen (用状态压缩解决N后问题)

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2136 Problem A Another n-Queen Problem I guess the n-queen problem is known by every person who has studied backtracking. In this problem you s

dp状态压缩

dp状态压缩 动态规划本来就很抽象,状态的设定和状态的转移都不好把握,而状态压缩的动态规划解决的就是那种状态很多,不容易用一般的方法表示的动态规划问题,这个就更加的难于把握了.难点在于以下几个方面:状态怎么压缩?压缩后怎么表示?怎么转移?是否具有最优子结构?是否满足后效性?涉及到一些位运算的操作,虽然比较抽象,但本质还是动态规划.找准动态规划几个方面的问题,深刻理解动态规划的原理,开动脑筋思考问题.这才是掌握动态规划的关键. 动态规划最关键的要处理的问题就是位运算的操作,容易出错,状态的设计也直

HDU3001(KB2-J 状态压缩dp)

Travelling Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 8103    Accepted Submission(s): 2642 Problem Description After coding so many days,Mr Acmer wants to have a good rest.So travelling is

2017盛大游戏杯 零件组装(状态压缩DP之巧妙枚举子集)

题目链接:2017盛大游戏杯 零件组装 题意: 有n个零件,给你相邻关系和排斥关系,每两块零件组装起来有一个代价,问最少的代价总和是多少. 题解: 考虑状态压缩,dp[i]表示i这个集合为一个零件块. 那么要枚举一下i的子集.O(3^n). 先要预处理一下每个集合的排斥个数和相邻个数,然后容斥一下就可以了. 1 #include<bits/stdc++.h> 2 #define mst(a,b) memset(a,b,sizeof(a)) 3 #define F(i,a,b) for(int