题意:
有一个ACM社团,有n个成员,每个成员的战斗力1或2.
现在这n个人都不认识,然后每一天都会有两个人相识(如果A和B相识,B和C相识 那么自然A和C相识).
现在每一天都要让你选出3个人,这三个人的战斗力必须>=5,而且这三个人必须都不认识。
问你每一天有多少种方案可以组合起来。
解析:
根据样例相识的关系越来越多,最后的方案肯定为0,所以我们算出的总的方案减去每一天相识的人组队的方案即可
首先我们统计出战斗力为1的人数cnt1,战斗力为2的人数cnt2
那么总的方案sum=
C
(
2
c
n
t
2
)
C\tbinom{2}{cnt2}
C(cnt22)*
C
(
1
c
n
t
1
)
C\tbinom{1}{cnt1}
C(cnt11)+
C
(
3
c
n
t
2
)
C\tbinom{3}{cnt2}
C(cnt23)
假设现在x和y相识,找到x和y的根节点 u 和 v
设p1为以u为根的集合中 战斗力为1的个数
设p2为以u为根的集合中 战斗力为2的个数
分为四种情况:
第一种:
在u集合种选战斗力为2的 ,在v集合种选战斗力为2的,在剩下不相识的集合种选战斗力为1的.方案数:p2[u]* p2[v] *(cnt1-p1[u]-p1[v])
第二种:
在u集合种选战斗力为2的 ,在v集合种选战斗力为1的,在剩下不相识的集合种选战斗力为2的.方案数:p2[u]* p1v] *(cnt2-p2[u]-p2[v])
第三种:
在u集合种选战斗力为1的 ,在v集合种选战斗力为2的,在剩下不相识的集合种选战斗力为2的.方案数:p1[u]* p2[v] *(cnt2-p2[u]-p2[v])
第四种:
在u集合种选战斗力为2的 ,在v集合种选战斗力为2的,在剩下不相识的集合种选战斗力为2的.方案数:p2[u]* p2[v] *(cnt2-p2[u]-p2[v])
设k=第一种+第二种+第三种+第四种 的方案数
那么每一天的方案数为 sum-k;
集合的关系就用并查集维护即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10000;
const int MOD=1e9+7;
int fa[N];
int a[N];
int size[N];
ll p1[N],p2[N];
ll cnt1,cnt2;
int t,u,v,n;
int find(int x)
{
if(fa[x]!=x) return fa[x]=find(fa[x]);
return fa[x];
}
ll qsc_2(ll a)
{
if(a<2) return 0;
return a*(a-1)/2%MOD;
}
ll qsc_3(ll a)
{
if(a<3) return 0;
return a*(a-1)*(a-2)/6%MOD;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
cnt1=cnt2=0;
for(int i=0;i<=n;i++) fa[i]=i;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==1) cnt1++,p1[i]=1,p2[i]=0;
else cnt2++,p2[i]=1,p1[i]=0;
}
ll sum=(((qsc_2(cnt2)%MOD)*cnt1%MOD)%MOD+(qsc_3(cnt2)%MOD))%MOD;
printf("%lld\n",sum%MOD);
for(int i=0;i<n-1;i++)
{
scanf("%d %d",&u,&v);
int x=find(u);int y=find(v);
ll k=0;
k+=(p2[x]*p2[y])%MOD*(cnt1-p1[x]-p1[y])%MOD;
k+=(p2[x]*p1[y])%MOD*(cnt2-p2[x]-p2[y])%MOD;
k+=(p1[x]*p2[y])%MOD*(cnt2-p2[x]-p2[y])%MOD;
k+=(p2[x]*p2[y])%MOD*(cnt2-p2[x]-p2[y])%MOD;
sum=((sum-k)%MOD+MOD)%MOD;
printf("%lld\n",sum%MOD);
fa[x]=y;p1[y]+=p1[x];p2[y]+=p2[x];
p1[x]=p2[x]=0;
}
}
}