第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这个情况即可。

计算:

最开始所有人都互相不是朋友答案为 \textrm{C}_{n}^{4} ,由于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),这里涉及组合数学

在同一集合时,输出上一轮的答案。

代码:

#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;
}
View Code

 参考博客:

https://blog.csdn.net/qq_41117236/article/details/99677875

https://www.cnblogs.com/1625--H/p/11359772.html

posted @ 2019-08-16 22:02  Young-children  阅读(164)  评论(0编辑  收藏  举报