题意:给出n个人,m个操作,每次操作可以把两个人和为朋友,朋友关系是相互的和传递的,问每次操作后,从这些人里挑出4个毫无关系的人的挑法有多少种。
思路:考虑每次合并减少了多少,减少的数目就是合并的两个集合的大小相乘再乘以剩下的不在一个集合的人中挑出两个人的数目。从不在一个集合里挑出两个人的挑法可以用任选两个的方案数减去在同一个集合挑两个人的挑法。
这道题当时想出了解法,但好久没写过并查集,忘了怎么在合并时顺便统计集合的大小,本身是签到题。
#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
const int N = 1e5 + 5;
ll f[N], u, v, sz[N], n, m;
ll F(ll x) {
return f[x] == x ? x : f[x] = F(f[x]);
}
int main() {
for(int i = 0; i < N; i++)
f[i] = i, sz[i] = 1;
cin >> n >> m;
ll ans = n * (n - 1) / 2 * (n - 2) / 3 * (n - 3) / 4, s = 0;
while(m--) {
cout << ans << endl;
cin >> u >> v;
ll x = F(u), y = F(v);
if(x == y)
continue;
f[x] = y;//y才是根节点!!!
ll a = sz[x], b = sz[y];
sz[y] += sz[x];//要用y!!!
ll k = n - a - b;
s -= (a * (a - 1) / 2 + b * (b - 1) / 2);
ans -= a * b * (k * (k - 1) / 2 - s);
s += (sz[y] * (sz[y] - 1)) / 2;
}
cout << ans << endl;
return 0;
}