Little W and Contest--------------------------------思维(组合数学+并查集+逆向思维)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
题意:

有一个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;
        }

    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值