HDU6795(HDU第三场多校).Little W and Contest(并查集)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6795
题意:
club中三个互相都不认识的人且三个人中能力值为2的至少有两人可以组成一个team。并且,A和B相识,B和C相识,那么A和C相识。一共有n-1次让两个不认识的人相识,求每次交友后可以组成多少种不同的team
解题思路:
交友问题很显示使用并查集,将相互认识的人放在同一个并查集中,同一个并查集中的人不能够成为队友。保存总的1能力值人数为num[0],总的2能力值人数为num[1]。每个并查集中pre[i][0]表示祖先为i点的并查集中的1能力值人数,pre[i][1]表示2能力值人数
队伍组成的情况有两种: 1 2 2 和 2 2 2
在所有人都不认识的情况下,

ll cnt=1ll*num[1]*(num[1]-1)/2 %mod*num[0]%mod + 1ll*num[1]*(num[1]-1)*(num[1]-2)/6 %mod;

注意去重,取余的位置和防止爆int,不要对分子先进行取模,防止造成分子除分母无法刚好除尽的情况
然后对于每次交友之后,形成不同集合之后,对于结果都是有影响的,要去除原本u和v所在的集合与这两个集合之外人组成team的数量

int fau=find(u),fav=find(v);
			int u1=pre[fau][0],u2=pre[fau][1];
			int v1=pre[fav][0],v2=pre[fav][1];
			int rt=unite(u,v);
			int n1=pre[rt][0];
			int n2=pre[rt][1];
			cnt-=1ll*u1*v2%mod*(num[1]-n2)%mod; if(cnt<0) cnt+=mod;//cnt=(cnt+mod)%mod;
			cnt-=1ll*u2*v1%mod*(num[1]-n2)%mod; if(cnt<0) cnt+=mod;//cnt=(cnt+mod)%mod;
			cnt-=1ll*u2*v2%mod*(n-n1-n2)%mod; if(cnt<0) cnt+=mod;//cnt=(cnt+mod)%mod;

同时也要注意u1*v2等等时可能爆int,要加ll

#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
#define maxn 110000
#define ll long long
const int mod=1e9+7;
int pa[maxn];
int pre[maxn][2];
int ab[maxn];
int num[2];
int n,q;
int t;
int find(int x){
	return x==pa[x]?x:pa[x]=find(pa[x]);
}
int unite(int x,int y){
	int a=find(x);
	int b=find(y);
	if(a==b)  return a;
	pa[b]=a;
	pre[a][0]+=pre[b][0];
	pre[a][1]+=pre[b][1];
	return a;
}
int main(){
	cin>>t;
	while(t--){
		memset(pre,0,sizeof(pre));
		num[0]=num[1]=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			pa[i]=i;
			scanf("%d",&ab[i]);
			if(ab[i]==1){
				num[0]++;
				pre[i][0]=1;
			}
			if(ab[i]==2){
				num[1]++;
				pre[i][1]=1;
			}
		}
		ll cnt=1ll*num[1]*(num[1]-1)/2 %mod*num[0]%mod + 1ll*num[1]*(num[1]-1)*(num[1]-2)/6 %mod;
		printf("%lld\n",cnt%mod);
		for(int i=1;i<=n-1;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			if(i>=n-2){
				printf("0\n");
				continue;
			}
			int fau=find(u),fav=find(v);
			int u1=pre[fau][0],u2=pre[fau][1];
			int v1=pre[fav][0],v2=pre[fav][1];
			int rt=unite(u,v);
			int n1=pre[rt][0];
			int n2=pre[rt][1];
			cnt-=1ll*u1*v2%mod*(num[1]-n2)%mod; if(cnt<0) cnt+=mod;//cnt=(cnt+mod)%mod;
			cnt-=1ll*u2*v1%mod*(num[1]-n2)%mod; if(cnt<0) cnt+=mod;//cnt=(cnt+mod)%mod;
			cnt-=1ll*u2*v2%mod*(n-n1-n2)%mod; if(cnt<0) cnt+=mod;//cnt=(cnt+mod)%mod;
			printf("%lld\n",cnt%mod);
		}
		
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Buyi.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值