第9场 E-All men are brothers(并查集)
题意:n个人,m次操作,每次操作使得两个人(x,y)成为朋友,朋友的关系是可以传递的,计算执行每次操作后,选择四个人两两都不是朋友的不同方案的数目。
数据范围:(n <= 100000,m <= 200000) (1 <= x <= n,1 <= y <= n,x≠y)
输入要求:
第一行包含两个整数,n和m
在下面的m行中,第i行包含两个整数x和y,这意味着第x个人和 y 这个人在第一轮交友。
第x个人和第y个人可能会在几个回合中结交朋友。
输出要求:
输出m + 1行,每行包含一个整数,即选出4个人的方案数。
在每轮操作开始前和结束后输出,因此有m + 1行。
思路:
我们把所有是朋友的分成一个集合,x所在集合为x集合,同理y集合,其它统一为z集合(里面有若干个并查集)。若x,y在同一个集合,则为(x+y)集合。
如果x,y不在同一集合,总共有x1+z3(x里面选1个,z里面选3个), y1+z3 ,x1+y1+z2 ,z4 ,这4种情况,
而x,y合并之后, 总共有 z4 , (x+y)1+z3【等价于 x1+z3, y1+z3】,所以如果需要合并,减去x1+y1+z2这个情况即可。
计算:
最开始所有人都互相不是朋友答案为 ,由于n比较大,需要用到 unsigned long long;
开始合并后:
若x,y不在同一集合,需要合并时,减去x1+y1+z2这种情况 ,
即 a*b*((n-a-b)*(n-a-b)-s+a*a+b*b)/2(a表示x集合人数,b表示y集合人数,s开始为总人数,更新为s+=2*a*b),这里涉及组合数学。
在同一集合时,输出上一轮的答案。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ll; const int N=1e5+10; int f[N],c[N]; ll Find(int x) { return f[x]==x?x:f[x]=Find(f[x]); } int main() { ios::sync_with_stdio(0); for(int i=0;i<N;i++) f[i]=i,c[i]=1; ll n; int m; cin>>n>>m; ll ans=n*(n-1)/2*(n-2)/3*(n-3)/4,s=n; while(m--){ cout<<ans<<endl; int u,v; cin>>u>>v; u=Find(u); v=Find(v); if(u==v||ans==0) continue; ll a=c[u],b=c[v]; ans-=a*b*((n-a-b)*(n-a-b)-s+a*a+b*b)/2; s+=2*a*b; c[v]+=c[u]; f[u]=v; } cout<<ans<<endl; return 0; }
参考博客: