链接:https://ac.nowcoder.com/acm/contest/889/E
来源:牛客网
题目描述
Amy asks Mr. B problem E. Please help Mr. B to solve the following problem.
There are n people, who don’t know each other at the beginning.
There are m turns. In each turn, 2 of them will make friends with each other.
The friend relation is mutual and transitive.
If A is a friend of B, then B is also a friend of A.
For example, if A is a friend of B, B is a friend of C, then A and C are friends.
At the beginning and after each turn, please calculate the number of ways to select four people from, such that any two of these four are not friends.
输入描述:
The first line contains two integers, n and m (n <= 100000, m <= 200000), which are the number of people, and the number of turns.
In the following m lines, the i-th line contains two integers x and y ( 1 <= x <= n, 1 <= y <= n, x ≠ y), which means the x-th person and the y-th person make friends in the i-th turn.
The x-th person and y-th person might make friends in several turns.
输出描述:
Output m+1 lines, each line contains an integer, which is the number of quadruples.
Output at the beginning and after each turn, so there are m+1 lines.
示例1
输入
复制
6 6
1 2
3 4
4 5
3 5
3 6
2 4
输出
复制
15
9
4
0
0
0
0
示例2
输入
复制
100000 0
输出
复制
4166416671249975000
说明
Don’t use int.
题目大意:
先输入两个整数
n
,
m
n,m
n,m,代表共有
n
n
n个人,
m
m
m次询问,最开始这
n
n
n个人相互独立,每个人属于一个集合,需先输出从这
n
n
n个人里选出
4
4
4个人,要求选出的
4
4
4个人人之间两两不在同一个集合中,问所有的情况数,对于
m
m
m次询问,每次输入两个整数
a
a
a,
b
b
b,意思是将
a
a
a所在的集合与
b
b
b所在的集合合并,并输出这时选出的
4
4
4个人人之间两两不在同一个集合的所有的情况数。
解题思路:
首先我们知道对于最开始的情况所有的方案数:
a
n
s
=
C
n
4
ans=C_n^4
ans=Cn4,而后对于
m
m
m次询问,我们先假设
a
a
a所在的集合为
A
A
A,
b
b
b所在的集合为
B
B
B,其他集合为
C
C
C,如果需要将集合
A
A
A与集合
B
B
B合并,那么对于所有的情况数,相应的少了在
A
A
A中选
1
1
1人,在
B
B
B中选
1
1
1人,在
C
C
C中选
2
2
2人的情况,因此仅需在
a
n
s
ans
ans的基础上减去这些情况即可,即:
a
n
s
=
a
n
s
−
C
A
1
−
C
B
1
−
C
C
2
ans=ans-C_A^1-C_B^1-C_C^2
ans=ans−CA1−CB1−CC2
而对于
C
A
1
C_A^1
CA1与
C
B
1
C_B^1
CB1我们仅需用并查集维护一下每个集合中的人数即可,而
C
C
2
C_C^2
CC2则需另外维护,我们先假设
n
u
m
=
C
n
2
num=C_n^2
num=Cn2,而可知
C
C
2
=
n
u
m
−
C
A
1
∗
C
C
1
−
C
B
1
∗
C
C
1
−
C
A
1
∗
C
B
1
C_C^2=num-C_A^1*C_C^1-C_B^1*C_C^1-C_A^1*C_B^1
CC2=num−CA1∗CC1−CB1∗CC1−CA1∗CB1,但还需记住每次合并后需将
n
u
m
num
num更新回去,即
n
u
m
=
n
u
m
+
C
A
+
B
1
∗
C
C
1
num=num+C_{A+B}^1*C_C^1
num=num+CA+B1∗CC1
代码:
//#pragma GCC optimize(3,"Ofast","inline")
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <bitset>
#include <set>
#include <utility>
#include <sstream>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define inf 0x3f3f3f3f
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define lep(i,l,r) for(int i=l;i>=r;i--)
#define ms(arr) memset(arr,0,sizeof(arr))
//priority_queue<int,vector<int> ,greater<int> >q;
const int maxn = (int)1e5 + 5;
const ll mod = 1e9+7;
ll jh[100100];
int f[100100];
ll C[100100][5];
void get_C() {
C[0][0]=1;
C[1][0]=C[1][1]=1;
for(int i=1;i<=100000;i++) {
C[i][0]=1;
for(int j=1;j<=min(i,4);j++) {
C[i][j]=C[i-1][j]+C[i-1][j-1];
}
}
}
int find(int x) {
return x==f[x]?x:find(f[x]);
}
void merge(int a,int b) {
int fa=find(a);
int fb=find(b);
if(fa!=fb) {
if(fa<fb) {
f[fb]=fa;
jh[fa]+=jh[fb];
jh[fb]=0;
}
else {
f[fa]=fb;
jh[fb]+=jh[fa];
jh[fa]=0;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
//freopen("out.txt", "w", stdout);
ios::sync_with_stdio(0),cin.tie(0);
get_C();
ll ans=1;
ll n,m;
ll num;
cin>>n>>m;
ans=C[n][4];
if(n<4) {
ans=0;
}
for(int i=1;i<=n;i++) {
f[i]=i;
jh[i]=1;
}
num=C[n][2];
cout<<ans<<endl;
ll a,b;
for(int i=1;i<=m;i++) {
cin>>a>>b;
if(find(a)==find(b)) {
cout<<ans<<endl;
continue;
}
ll jha=jh[find(a)];
ll jhb=jh[find(b)];
num=num-(n-jha-jhb)*jha-(n-jha-jhb)*jhb-jha*jhb;
if(num<0) num=0;
merge(a,b);
ans-=(jha*jhb*num);
jha=jh[find(a)];
num+=jha*(n-jha);
if(ans<0) ans=0;
cout<<ans<<endl;
}
return 0;
}