UOJ#192. 【UR #14】最强跳蚤
题目链接 http://uoj.ac/problem/192
暑期课第二天
树上问题进阶
具体内容看笔记博客吧
题意
n个节点的树T 边有边权w 求满足(u, v)上所有边权乘积为完全平方数的路径有多少条
看到“所有边权乘积为完全平方数” 想到完全平方数的特殊性
就是分解质因数后 质因数指数都为偶数
然后就想到分解边权质因数+判质路径边权奇偶性
后者由于奇数偶数的和的规律 可以使用抑或
偶就表示为0 奇就表示为一
那么如何存储呢?
状压?
空间之大 状压压不下
所以hash
对每一个要用的质数 取一个 [1, 2 ^ 64] 的随机数
出现一次就抑或一次即可
然后。。。
题意
n个节点的树T 边有边权w 求满足(u, v)上所有边权抑或和为0的路径有多少条
前缀和最常用的两种 一是累加和 二是抑或和
明显可以使用前缀和
又由于 a ^ a = 0
对于一条路径 (路径两端点的lca) 到 (根节点)的那一段抑或两次没啦
所以如果(u, v)上所有边权抑或和为0
那么他们的抑或前缀和相等
以下附莫名被ex扣下3分的辣鸡代码
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstdlib> 4 #include <ctime> 5 #include <map> 6 using namespace std; 7 const int N = 2e5 + 5; 8 const int M = 1e4 + 5; 9 10 int n, m; 11 struct Edge{ 12 int u, v; 13 long long w; 14 int next; 15 }edge[N << 1]; 16 int esize, head[N]; 17 long long p[M + 5], ps; 18 bool np[M]; 19 long long num[N]; 20 map<int, long long> rf; 21 22 inline void addedge(int x, int y, long long z){ 23 edge[++esize] = (Edge){x, y, z, head[x]}; 24 head[x] = esize; 25 } 26 27 inline void p_cal(){ 28 for(int i = 2; i < M; i++){ 29 if(!np[i]) p[++ps] = i; 30 for(int j = 1; j <= ps && i * p[j] < M; j++){ 31 np[i * p[j]] = 1; 32 if(!(i % p[j])) break; 33 } 34 } 35 } 36 37 inline void build(int x, int fa){ 38 for(int i = head[x]; i != -1; i = edge[i].next){ 39 int v = edge[i].v; 40 if(v == fa) continue; 41 num[v] = num[x] ^ edge[i].w; 42 build(v, x); 43 } 44 } 45 46 int main(){ 47 srand(time(NULL)); 48 scanf("%d", &n); 49 p_cal(); 50 for(long long i = 1; i <= ps; i++) 51 rf[p[i]] = ((long long)rand() << 30) + rand(); 52 //rd续命法 53 long long res; 54 int x, y, z; 55 for(int i = 0; i <= n; i++) head[i] = -1; 56 for(int i = 1, x, y, z; i < n; i++){ 57 scanf("%d%d%d", &x, &y, &z); 58 res = 0; 59 int tmp = z; 60 for(int j = 1; j <= ps && p[j] * p[j] <= tmp; j++) 61 while(!(z % p[j])) 62 res ^= rf[p[j]], z /= p[j]; 63 if(z != 1) { 64 if(!rf[z]) 65 rf[z] = ((long long)rand() << 30) + rand(); 66 res ^= rf[z]; 67 } 68 addedge(x, y, res); addedge(y, x, res); 69 } 70 71 build(1, -1); 72 sort(num + 1, num + n + 1); 73 long long ans = 0; 74 for(int i = 1, j; i <= n; i = j){ 75 j = i; 76 while(num[j] == num[i] && j <= n) j++; 77 ans += (long long)(j - i) * (j - i - 1); 78 } 79 printf("%lld", ans); 80 return 0; 81 }