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<<"\n"; 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<<"\n"; } return 0; }