淘先锋技术网

首页 1 2 3 4 5 6 7

题目链接:https://ac.nowcoder.com/acm/contest/889/E

题意:一共有n个人,最初他们彼此之间都不认识,然后有m次操作,每次操作让两个人x,y成为朋友,朋友关系可以传递,现在要求最开始及每次操作后选4个人,他们彼此之间都不是朋友的方案数。

(n <= 100000, m <= 200000)   ( 1 <= x <= n, 1 <= y <= n, x ≠ y)

思路:弄一个并查集把是朋友的一堆人放在一起。最开始预处理一个C(n,4),作为最开始的方案数,然后定义一个变量s作为在同个朋友集合里挑两个人的方案数。每有两个非朋友关系的人成为朋友,我们就要减去对应的数量:合并的两个集合大小相乘,再乘以其他集合里选出两个不在一个集合内的方案数。从其他集合中选出2个不在一个集合内的方案数可以先计算任选2个的方案数,再减去来自同一个集合的方案数,此时上面的s就起作用了。

下面是AC代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef unsigned long long LL;     //long long对于100000取4会爆
const int N=3e6+10;
LL n,m,x,y,a,b,t;
LL fa[N],num[N];        //num数组记录每个集合的元素个数
inline int fin(int x)   
{
    if(x!=fa[x])
        return fa[x]=fin(fa[x]);
    else return x;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    LL ans=n*(n-1)/2*(n-2)/3*(n-3)/4;
     for(int i=1; i<=n; i++)
        fa[i]=i,num[i]=1;
    printf("%lld\n",ans);
    LL s=0;
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&x,&y);
        a=fin(x),b=fin(y);
        if(a==b||ans==0)
        {
            printf("%lld\n",ans);
            continue;
        }
        t=num[a]*num[b];
        s-=num[a]*(num[a]-1)/2+num[b]*(num[b]-1)/2;//s减去在x或y集合中拿两个的情况
        ans-=t*(n-num[a]-num[b])*(n-num[a]-num[b]-1)/2;
        ans+=s*t;
        s+=(num[a]+num[b])*(num[a]+num[b]-1)/2;   //加上在新集合中拿两个的情况
        fa[a]=b;                                  //合并
        num[b]=num[a]+num[b];
        printf("%lld\n",ans);
    }
}

迟迟钟鼓初长夜,耿耿星河欲曙天。