2019牛客暑期多校训练营(第九场)-E All men are brothers

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

题意:n个人,m次操作,每次合并两个人,求合并后找出4个人,且两两不在一个集合的方案数。

思路:最开始有C[n][4]种方案,每次合并我们计算出减少了多少方案数即可。减少的为本次合并的两个集合的大小的乘积,再乘以从剩余集合中选出两个不在一个集合的方案数,这可以通过算出任意两个人的方案数,减去,在同一个集合的两个人的方案数得到。集合的大小通过并查集操作维护,并用前缀和思想记录此时选出在同一个集合的两个人的方案数sum。预处理得到组合数,只用预处理到C(n,4)即可。总复杂度为O(mlogn)。

AC代码:

#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;

typedef long long LL;
const int maxn=1e5+5;
int n,m,root[maxn],siz[maxn];
LL ans,sum,C[maxn][5];

void init(){
    C[1][0]=1,C[1][1]=1;
    C[2][0]=1,C[2][1]=2,C[2][2]=1;
    C[3][0]=1,C[3][1]=3,C[3][2]=3,C[3][3]=1;
    for(int i=4;i<maxn;++i){
        C[i][0]=1;
        for(int j=1;j<=4;++j)
            C[i][j]=C[i-1][j-1]+C[i-1][j];
    }
}

int getr(int k){
    if(root[k]==k) return k;
    else return root[k]=getr(root[k]);
}

int main(){
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
        root[i]=i,siz[i]=1;
    ans=C[n][4];
    printf("%lld\n",ans);
    while(m--){
        int x,y;
        scanf("%d%d",&x,&y);
        int xr=getr(x),yr=getr(y);
        if(xr==yr){
            printf("%lld\n",ans);
            continue;
        }
        LL tx=0,ty=0;
        if(siz[xr]>=2) tx=C[siz[xr]][2];
        if(siz[yr]>=2) ty=C[siz[yr]][2];
        sum-=tx+ty;
        ans-=1LL*siz[xr]*siz[yr]*(C[n-siz[xr]-siz[yr]][2]-sum);
        printf("%lld\n",ans);
        root[yr]=xr;
        siz[xr]+=siz[yr];
        sum+=C[siz[xr]][2];
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/FrankChen831X/p/11362450.html