2019牛客暑期多校训练营(第九场)E.All men are brothers(并查集+排列组合)

题意:现在有n个集合 每个集合大小为1 现在你可以把集合合并m次 每次会告诉你哪个集合合并 让你输出每次从不同的四个集合里各选出四个的组合方案

思路:我们可以想到用并查集模拟集合的合并 对于方案数 我们可以发现 其实就是合并之前的答案 减掉两个集合内的数的组合的方案数(详情理解代码)

#include <bits/stdc++.h>
using namespace std;
const double pi = acos(-1.0);
const int N = 1e6+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
typedef unsigned long long ll;
const ll mod = 1e7+9;
ll f[N],size[N];
ll find(ll x){
    if(x!=f[x])
        f[x]=find(f[x]);
    return f[x];
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    ll n,m; cin>>n>>m;
    ll ans=(__int128)(n-1)*n/2*(n-2)*(n-3)/12; //组合数 
    for(ll i=0;i<N;i++){
        f[i]=i;
        size[i]=1;
    }
    ll sum=n; //记录有多少种两两组合的方案(这里是可重复的) 
    cout<<ans<<"
";
    for(ll i=1;i<=m;i++){
        ll x,y; cin>>x>>y;
        ll xx=find(x); ll yy=find(y);
        if(xx!=yy){
            sum-=(size[xx]*size[xx]);
            sum-=(size[yy]*size[yy]);
            ll tmp=size[xx]*size[yy]*((n-size[xx]-size[yy])*(n-size[xx]-size[yy])-sum)/2;
            f[xx]=yy;
            size[yy]+=size[xx];
            sum+=(size[yy]*size[yy]);
            ans-=tmp;            
        }
        cout<<ans<<"
";
    }
    return 0;
}
原文地址:https://www.cnblogs.com/wmj6/p/11359841.html