题意
最初有 $n$ 个人且互不认识,接下来 $m$ 行,每行有 $x,y$,表示 $x$ 和 $y$ 交朋友,朋友关系满足自反性和传递性,每次输出当前选取4个人且互不认识的方案数。
分析
并查集维护集合的并。
考虑两个集合的并对答案的影响,总的来说就是减去集合x中选一个、集合y中选一个,剩下的选两个(这两个来自不同集合)。
代码实现上体会 $cs$ 变量的作用。
#include<bits/stdc++.h> using namespace std; typedef unsigned long long ll; const int maxn = 1e5 + 10; int n, m; int fa[maxn], rk[maxn]; ll C4(ll x) { return x * (x-1)/2 * (x-2)/3 * (x-3)/4; } ll C2(ll x) { return x * (x-1) / 2; } void init() { for(int i = 1;i <= n;i++) { fa[i] = i; rk[i] = 1; } } int find(int x) { if(x != fa[x]) fa[x] = find(fa[x]); return fa[x]; } void unite(int x, int y) { int rx = find(x); int ry = find(y); if(rx == ry) return; fa[rx] = ry; rk[ry] += rk[rx]; } int main() { scanf("%d%d", &n, &m); ll ans = C4(n); printf("%lld ", ans); init(); ll cs = 0; //每个集合中取两个的方案数的和 for(int i = 0;i < m;i++) { int a, b; scanf("%d%d", &a, &b); if(find(a) == find(b)) { printf("%lld ", ans); continue; } int rka = rk[find(a)], rkb = rk[find(b)]; ll tmp1 = 1LL * rka * rkb; ll tmp2 = C2(n-rka-rkb) - (cs - (C2(rka)+C2(rkb))); cs = cs - (C2(rka)+C2(rkb)) + C2(rka+rkb); ans -= tmp1 * tmp2; printf("%lld ", ans); unite(a, b); } return 0; }