E-All men are brothers 2019牛客暑假多校第九场 (组合,并查集)

题意:给你n个人(编号1~n),m个关系
每对关系表示 a, b 互相认识,现在你要输出在给出关系之后,从所有人中选出4个互不认识的人的组合个数
思路:对于关系处理我们肯定能够想到并查集。然后每次找到有多少对不同的人数,输出他们的组合数。我一开始是顺着想,统计所有集合数目,然后计算组合数把他们加起来,结果自己给加晕了。

就像HDU-6425 Rikka with Badminton (组合问题,快速幂)这种组合问题,我们正向去想麻烦就要反正去想(容斥),减去每次合并导致减少的组合数。

合并减少的组合数即是:合并的两个集合大小相乘乘, 再乘以从其他集合中选出2个不在一个集合内的方案数。

从其他集合中选出2个不在一个集合内的方案数,可以先计算任选2个的方案数,再减去来自同一个集合的方案数。


#include<bits/stdc++.h> typedef long long int ll; const int maxn = 1e6 + 10; const int maxm = 1e5 + 10; using namespace std; ll pre[maxn],siz[maxn],n,m; ll find(ll x){ return x == pre[x] ? x : pre[x] = find(pre[x]); } void Union(ll x,ll y){ ll fx = find(x); ll fy = find(y); if(fx != fy) { pre[fy] = fx; siz[fx] += siz[fy]; } } void init(){ for(ll i = 1;i <= n;i++){pre[i] = i;siz[i] = 1;} } int main(){ cin >> n >> m; init(); ll tot = n * (n - 1) / 2 * (n - 2) / 3 / 2 * (n - 3) / 2; ll dif = n * (n - 1) / 2; printf("%lld\n", tot); for(ll i = 1;i <= m;i++){ ll x,y; scanf("%lld%lld",&x,&y); ll fx=find(x); ll fy=find(y); if(fx==fy){ printf("%lld\n",tot); continue; } ll uplus = siz[fx] + siz[fy];//只是存储加、积 ll umult = siz[fx] * siz[fy]; tot -= (umult) * ((dif - umult) - (uplus)*(n - (uplus)));//当前剩余组合数,即答案 dif -= (umult);//用来存储来自2个不在同一集合的方案数 Union(x,y); printf("%lld\n", tot); } return 0; }

 

posted @ 2019-08-18 16:14  Tianwell  阅读(154)  评论(0编辑  收藏  举报