2019牛客多校第⑨场E All men are brothers(并查集+组合数学)

原题:https://ac.nowcoder.com/acm/contest/889/E

思路:

做并查集,维护每个集合大小,初始化操作前的总方案数,每次合并两个集合时减少的数量=合并的两个集合大小相乘, 再乘以从其他集合中选出2个不在一个集合内的方案数。

从其他集合中选出2个不在一个集合内的方案数=任选2个的方案数-来自同一个集合的方案数

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
ll fa[maxn],cnt[maxn];
ll n,m,tot;
ll find(ll x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main(){
	cin>>n>>m;
	for(ll i=1;i<=n;i++){
		fa[i]=i;
		cnt[i]=1;
	}
	tot=n;
	ll x,y;
	ll res=n*(n-1)*(n-2)/2/3;
	if(res%4==0){
		res=res/4*(n-3);
	}
	else res=(n-3)/4*res;
	ll del=0;//计算所有>=2的集合中选两个的方案数之和 
	printf("%lld\n",res);
	for(ll i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		if(find(x)!=find(y)){
			if(tot<=4) res=0;
			else{
				ll s1=cnt[fa[x]],s2=cnt[fa[y]];
				ll temp=(n-s1-s2)*(n-s1-s2-1)/2; //C(tot-2,2);
				ll temp2=del;
				temp2-=s1*(s1-1)/2+s2*(s2-1)/2;
				del-=s1*(s1-1)/2+s2*(s2-1)/2;//先减,等一会加上合并之后的
				res-=s1*s2*(temp-temp2);
				ll s3=s1+s2;
				del+=s3*(s3-1)/2;
				ll fx=fa[x],fy=fa[y];
				fa[fx]=fy;
				cnt[fy]+=cnt[fx];
			}
			tot--;
		}
		printf("%lld\n",res);
	}
}
posted @ 2019-08-15 18:48  UCPRER  阅读(141)  评论(0编辑  收藏  举报