一道好题,本来是做几道启发式合并玩玩,没想到是个哈希。
这一道题需要维护连通性,显然想到使用并查集。
如果两个点在某个图内的父亲相同,显然这两个点就连通了。
但是如果每链接一对点我们就遍历所有点对然后判断父亲,显然爆炸。
于是考虑借鉴一下 CSP 2022 T3 的思路,对于每个点处理一个值 $weight$。
我们再对每张图中的每个点搞一个随机值 $w$,如果 $i$ 的祖先是 $j$,那么给 $weight_i$ 加上 $w_j$ 即可。
这样我们就把每个点的 $weight$ 设成了它在所有的图中祖先节点的权值之和,然后维护有多少对 $weight$ 相等就行了。
这样错误的可能性很小,因为你很难构造出随机的方程,使得 $a_1+a_2+a_3+\cdots +a_d=b_1+b_2+b_3+\cdots b_d$,并且还有一大堆连通性的要求。
但我第一发的模数是 $998244353$,WA 掉一个点,所以模数尽量开大点。
#include <bits/stdc++.h> #define int long long using namespace std; const int N = 300005, mod = 998244353; #define For(i, a, b) for (int i = (a); i <= (b); ++ i) inline void r (int &x) { int s = 0, w = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = getchar (); } while (ch >= '0' && ch <= '9') { s = s * 10 + ch - 48; ch = getchar (); } x = s * w; } int d, n, m; int w[201][5001], fa[201][5001], sz[201][5001]; int weight[5001]; unordered_map <int, int> b; vector <int> v[201][5001]; int find (int k, int x) { if (fa[k][x] != x) { fa[k][x] = find (k, fa[k][x]); return fa[k][x]; } else return x; } signed main () { srand (time (0) ); r (d); r (n); r (m); For (i, 1, d) { For (j ,1, n) { w[i][j] = (long long) rand () * (long long) rand () % 129319283324; weight[j] += w[i][j]; v[i][j].push_back (j); fa[i][j] = j; sz[i][j] = 1; } } For (i, 1, n) b[weight[i] ] ++; int ans = n; For (i, 1, m) { int x, y, k; r (x); r (y); r (k); int fx = find (k, x), fy = find (k, y); if (fx == fy) { cout << ans << "\n"; continue; } if (sz[k][fx] > sz[k][fy]) swap (fx, fy); fa[k][fx] = fy; sz[k][fy] += sz[k][fx]; for (int i : v[k][fx]) { ans -= (-- b[weight[i] ]) * 2; weight[i] -= w[k][fx]; weight[i] += w[k][fy]; ans += b[weight[i] ] * 2; b[weight[i] ] ++; v[k][fy].push_back (i); } v[k][fx].clear (); cout << ans << "\n"; } return 0; }
标签:weight,fa,int,Bajtocja,ONTAK2015,P8026,long,fx,fy From: https://www.cnblogs.com/Xy-top/p/17500670.html