2020暑假杭电多校第三场:05/E : Little W and Contest【路径压缩并查集+数学式子】

题目链接:杭电problems6795

题解太过于玄乎,我写一下直接用数学式子解决的方法把:

我们把1能力的选手和2能力的选手分成两堆:

能力1选手数量为:n
能力2选手数量为:m
每一次操作其实就是两个连通块给连起来对不对?我们初始化:每一个点都是一个连通块,能力为1的选手的连通块我们在根节点用a=1,b=0表示这个连通块的能力为1选手数量为1,能力为2选手数量为0;

那么我们0次操作后的答案是很容易求出来的:C(2,m)*n+C(3,m)。
我们用last变量存上一次操作的答案。

在某次操作,我们把a1,b1属性的连通块和a2,b2属性的连通块连在一起,那么就是说:
1 a1 , a2 , n - a1 - a2
2 b1 , b2 , m - b1 - b2
当把三块分出来,我们可以捋清楚了以下四个情况:
原本b1一个,b2一个,(n-a1-a2)一个可以,现在不行了
原本b1一个,b2一个,(m-b1-b2)一个可以,现在不行了
原本b1一个,a2一个,(m-b1-b2)一个可以,现在不行了
原本a1一个,b2一个,(m-b1-b2)一个可以,现在不行了

那么last=last-b1b2(n-a1-a2)-b1b2(m-b1-b2)-b1a2(m-b1-b2)-b2a1(m-b1-b2)

Code:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <bitset>
#include <iomanip>
//
using namespace std;
const int INF = 0x3f3f3f3f;//1.06e9大小
const int mod1 = 1e9 +7;
const int mod2 = 998244353;
const int mod3 = 1e9;
const double PI = acos(-1);
const double eps =1e-8;
typedef unsigned long long ull;
typedef long long ll;
typedef unsigned uint;
const int sq5=616991993;
#define ms(x, n) memset(x,n,sizeof(x))
#define debug printf("***debug***\n")
#define pii pair<int ,int>
#define X first
#define Y second
#define pb push_back
#define ls(i) i<<2
#define rs(i) i<<2|1
#define lowbit a&(-a)
/*
*/
int t,tot;
ll n,m,last;
const int maxn = 1e5+100;
struct Root
{
    ll a,b;
}root[maxn];

int fa[maxn];
int getfather(int k){if(fa[k]==k)return k;return fa[k]=getfather(fa[k]);}
void out(ll a1 ,ll b1 ,ll a2 ,ll b2)
{
    ll res=0;
    res=(res+b1*b2*(n-a1-a2))%mod1;
    res=(res+b1*b2*(m-b1-b2))%mod1;
    res=(res+b1*a2*(m-b1-b2))%mod1;
    res=(res+b2*a1*(m-b1-b2))%mod1;
    last=(last-res+mod1)%mod1;
    printf("%lld\n",last);
}
void mer(int from ,int to )
{//连接函数
    int x=getfather(from);
    int y=getfather(to);
    fa[x]=y;
    out(root[y].a ,root[y].b ,root[x].a ,root[x].b );
    root[y].a+=root[x].a;
    root[y].b+=root[x].b;
}
int main()
{
    cin>>t;
    while(t--)
    {
        n=0,m=0;//n个1,m个2
        scanf("%d",&tot);
        for(int i=1;i<=tot;++i)
        {
            int num=0;
            scanf("%d",&num);
            fa[i]=i;
            if(num==1)
            {
                ++n;
                root[i]={1,0};
            }
            else
            {
                ++m;
                root[i]={0,1};
            }
        }
        last=(m*(m-1)*(m-2)/6)%mod1+((m*(m-1)*n)/2)%mod1;
        last %=mod1;
        printf("%lld\n",last);

        for(int i=1;i<tot;++i)
        {//询问
            int u,v;//u的root连到v的root上
            scanf("%d %d",&u,&v);
            mer(u,v);
        }
    }

    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值