题目链接:https://ac.nowcoder.com/acm/contest/889/E
题意:有n个人,开始互不是朋友,有m次操作,每次使得两人是朋友,(朋友具有传递性)。
问:从所有人中选出4个人,这四个人两两不是朋友的选择方案有多少种?对于每次操作输出一次
思路:现场做的时候虽然也是用的并查集和组合数,但是就是一直超时,可能是常数太大了吧,加上各种优化之后还是T到结束
赛后题解一处,还是优化不够到家啊,
考虑每次合并带来的贡献,
设一个集合人数为x,另一个为y,剩下的为z,合并之后为xy,
合并时,总的方法数会
1、减去x选1个,z选3个
2、减去y选1个,z选3个
3、减去x选1个,y选1个,z选2个
合并之后总的方法数会
4、加上xy选一个,z选3个
可以发现1,2 和 4,是抵消的,所以只考虑3即可
为了减少除法运算,可以维护一个选2个的组合数,用+-来维护
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
ll f[maxn], siz[maxn];
int getf(int x){
return x == f[x] ? x : f[x] = getf(f[x]);
}
__int128 sum, pre;
void merg1e(int x, int y) {
f[x] = y;
siz[y] += siz[x];
}
int main(){
ll n, q;
scanf("%lld %lld", &n, &q);
for(int i = 0; i <= n; i++) f[i] = i, siz[i] = 1;
sum = (__int128)n * (n - 1) * (n - 2) * (n - 3) / 24;
pre = n * (n - 1) / 2;
printf("%lld\n", sum);
while(q-- ){
int l, r;
scanf("%d %d", &l, &r);
int x = getf(l);
int y = getf(r);
if(x == y) printf("%lld\n",sum);
else {
ll a = siz[x], b = siz[y];
ll num = n - a - b;
ll tmp = pre - a * num - b * num - a * b;
sum -= a * b * tmp;
pre = tmp + (a + b) * num;
printf("%lld\n", sum);
merg1e(x, y);
}
}
return 0;
}