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; }
朋友们,无论这个世界变得怎样,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。