题目链接: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;
}