C 牛牛的无向图
题目地址:
基本思路:
我们考虑离线每次查询的,
在最小生成树的过程中,我们每次是从小往大加边的,
同样将也按从小到大排序,
每次加到一个比当前要大的边的时候,
之前的那些已经连上的边一定是都比小的,
所以在这些联通块中的点,两两之间的一定是比小的,也就是符合条件的,
那么这时符合条件的点对的数量就是(为每个联通块的大小),
每次并查集合并时维护这个结果就行了。
参考代码:
#pragma GCC optimize(2) #pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; #define IO std::ios::sync_with_stdio(false); cin.tie(0) #define ll long long #define SZ(x) ((int)(x).size()) #define all(x) (x).begin(), (x).end() #define rep(i, l, r) for (int i = l; i <= r; i++) #define per(i, l, r) for (int i = l; i >= r; i--) #define mset(s, _) memset(s, _, sizeof(s)) #define pb push_back #define pii pair <int, int> #define mp(a, b) make_pair(a, b) #define INF 0x3f3f3f3f inline int read() { int x = 0, neg = 1; char op = getchar(); while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); } while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); } return neg * x; } inline void print(int x) { if (x < 0) { putchar('-'); x = -x; } if (x >= 10) print(x / 10); putchar(x % 10 + '0'); } unsigned int SA, SB, SC; int n, m, q, LIM; unsigned int rng61() { SA ^= SA << 16; SA ^= SA >> 5; SA ^= SA << 1; unsigned int t = SA; SA = SB; SB = SC; SC ^= t ^ SA; return SC; } const int maxn = 1e6 + 10; ll u[maxn],v[maxn],w[maxn],L[maxn]; void gen() { scanf("%d%d%d%u%u%u%d", &n, &m, &q, &SA, &SB, &SC, &LIM); for (int i = 1; i <= m; i++) { u[i] = rng61() % n + 1; v[i] = rng61() % n + 1; w[i] = rng61() % LIM; } for (int i = 1; i <= q; i++) { L[i] = rng61() % LIM; } } ll par[maxn],sz[maxn]; ll find(ll x){ return par[x] == x ? x : par[x] = find(par[x]); } bool same(ll x,ll y){ return find(x) == find(y); } struct Edge{ ll u,v,w; bool operator < (const Edge &no) const { return w < no.w; } }; vector<Edge> edge; signed main() { gen(); for (int i = 1; i <= n; i++) par[i] = i, sz[i] = 1; rep(i, 1, m) edge.push_back({u[i], v[i], w[i]}); sort(L + 1, L + 1 + q); ll pos = 1,ans = 0,res = 0; sort(all(edge)); for (auto it : edge) { if (!same(it.u, it.v)) { while (it.w > L[pos] && pos <= q) { ans ^= res; pos++; } ll x = find(it.u),y = find(it.v); res -= sz[x] * (sz[x] - 1ll) / 2ll + sz[y] * (sz[y] - 1ll) / 2ll; // 消除要联通的这两个联通块的贡献; par[x] = y; sz[y] += sz[x]; res += sz[y] * (sz[y] - 1ll) / 2ll;// 加上新生成的这个联通块的贡献; } } rep(i,pos,q) ans ^= res; cout << ans << '\n'; return 0; }