zoukankan      html  css  js  c++  java
  • 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
    ",ans);
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            int xr=getr(x),yr=getr(y);
            if(xr==yr){
                printf("%lld
    ",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
    ",ans);
            root[yr]=xr;
            siz[xr]+=siz[yr];
            sum+=C[siz[xr]][2];
        }
        return 0;
    }
  • 相关阅读:
    POJ题目分类
    最短路&记录记录记录路径
    博弈论
    生成树模板总结
    弱鸡的暑假图论安排
    面试随缘做题--day2
    面试随缘做题---day1
    PAT第四章速刷
    PAT第二章知识点快速复习
    sql语句快速复习
  • 原文地址:https://www.cnblogs.com/FrankChen831X/p/11362450.html
Copyright © 2011-2022 走看看