牛客练习赛68C 牛牛的无向图(并查集)
题:https://ac.nowcoder.com/acm/contest/7079/C
题意:定义 d(u,v) 表示在无向图中点 u 能到达点 v 的所有路径中权值最小的路径的权值(一条路径的权值是这个路径包含的边的权值的最大值),q个询问 每次问有多少个d(u,v)<=L,求q个询问答案的异或值
分析:对于每个询问给定的限制L,我们只要考虑小于L权值的边组成的图会造成多大贡献即可(因为要选权值最小,要是有大边加上u和v,也不会选它)。对于每一条小于等于L的边,若这条边作为权值,那么肯定是作为“已形成图”的俩连通分量的桥,贡献就为size[u]*size[v],用并查集维护即可。
由于是考虑大小关系的,所以把边的权值和询问的L作为权值排序查询即可。
#include<bits/stdc++.h> using namespace std; #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r #define pb push_back #define ull unsigned long long #define pii pair<int,int> #define MP make_pair typedef long long ll; const ll INF=1e18; const int inf=0x3f3f3f3f; const int M=1e6+6; const int mod=199999; int u[M],v[M],w[M],L[M],f[M],sz[M]; struct node{ int u,v,w,op,id; }e[M]; ll ans[M],now=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; } 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; } } int Find(int x){ return x==f[x]?x:f[x]=Find(f[x]); } void Merge(int x,int y){ x=Find(x),y=Find(y); if(x==y) return ; now+=sz[x]*sz[y]; f[y]=x; sz[x]+=sz[y]; } int main(){ gen(); for(int i=1;i<=m;i++) e[i].u=u[i],e[i].v=v[i],e[i].w=w[i],e[i].op=1; for(int i=1;i<=q;i++) e[i+m].id=i,e[i+m].w=L[i],e[i+m].op=2; int tot=m+q; for(int i=1;i<=n;i++) f[i]=i,sz[i]=1; sort(e+1,e+1+tot,[&](node A,node B){ if(A.w==B.w) return A.op<B.op; return A.w<B.w; }); for(int i=1;i<=tot;i++){ if(e[i].op==1) Merge(e[i].u,e[i].v); else ans[e[i].id]=now; } ll res=0; for(int i=1;i<=q;i++) res^=ans[i]; printf("%lld",res); return 0; }