original link - https://ac.nowcoder.com/acm/contest/889/E
题意:
给出n个人,开始每个人独立。然后每次操作选择两个人,连接两个人所在的集合。问每次操作后,我选择4个人不在一个集合的方案数。
解析:
最开始的时候,A4表示选择4个人的方案数,为:Cn4,A3,A2,A1同理。
然后维护连接两个集合的影响,设两个集合的大小为:x,y。
定义F(n,x)为选择n个人且包括了x的方案数,F(n,y)同。
那么新的A4应该为:A4−F(4,x∨y)+(x+y)F(3,¬x∧¬y)
我会减去选择其中任意一个的方案,加上选择合并后的那堆再另外选择3堆的方案
F(4,x∨y)=xF(3,¬x)+yF(3,¬y)−xyF(2,¬x∧¬y)F(3,¬x)=A3−F(3,x)F(3,x)=xF(2,¬x)F(2,¬x)=A2−F(2,x)F(2,x)=xF(1,¬x)F(1,¬x)=A1−x...F(2,¬x∧¬y)=A2−F(2,x∨y)...
然后维护一下A3,A2:
A3−F(3,x∨y)+(x+y)F(2,,¬x∧¬y)...
反正到最后每个东西都是可以推出来的,每次输出一下A4就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define LLL __int128
LLL A4,A3,A2,A1;
const int maxn=1e5+9;
int fa[maxn];
int siz[maxn];
int fin(int a){
return fa[a]==a?a:fa[a]=fin(fa[a]);
}
int main(){
LL n,m;
scanf("%lld%lld",&n,&m);
int sum=n;
LLL One=1;
A1=n,A2=n*(n-1)/2,A3=One*A2*(n-2)/3,A4=One*A3*(n-3)/4;
rep(i,1,n)fa[i]=i,siz[i]=1;
printf("%lld\n",(LL)A4);
while(m--){
int a,b;scanf("%d%d",&a,&b);
int f1=fin(a),f2=fin(b);
if(f1==f2){
printf("%lld\n",(LL)A4);
continue;
}
LLL x=siz[f1],y=siz[f2];
fa[f1]=f2;
siz[f2]+=siz[f1];
sum--;
if(sum<4){
printf("0\n");
A4=0;
continue;
}
LLL _1notx=A1-x,
_1noty=A1-y,
_1notxnoty=A1-x-y,
_2xory=x*_1notx-x*y+y*_1noty,
_2notxnoty=A2-_2xory,
_2x=x*_1notx,
_2y=y*_1noty,
_2notx=A2-_2x,
_2noty=A2-_2y,
_3x=x*_2notx,
_3y=y*_2noty,
_3notx=A3-_3x,
_3noty=A3-_3y,
_3xory=x*_2notx-x*y*_1notxnoty+y*_2noty,
_3notxnoty=A3-_3xory,
_4xory=x*_3notx-x*y*_2notxnoty+y*_3noty;
A4-=_4xory;
A4+=(x+y)*_3notxnoty;
A3-=_3xory;
A3+=(x+y)*_2notxnoty;
A2-=_2xory;
A2+=(x+y)*_1notxnoty;
printf("%lld\n",(LL)A4);
}
}