2019牛客暑期多校训练营 (第九场) E All men are brothers 【并查集+思维】

题目描述

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

示例输出1 

15
9
4
0
0
0
0

示例输入2 

100000 0

示例输出2 

4166416671249975000

说明 

Don't use int.

题目大意:

有n个人,进行m次操作。每次操作使得两个人成为朋友,朋友的关系是可以传递的,即当两个人成为朋友后,与这两个人是朋友的所有人都成为了朋友。

计算执行每次操作后,选择四个人两两都不是朋友的不同方案的数目。

分析:

由题目中的操作以及关系的传递性,可以想到用并查集维护,同时维护每个集合中元素的个数。

初始状态下,方案数为 C_n^4 ,我们考虑每次操作减去的数。

每次减去的方案是:在当前两个集合中各选一个,此外在其他集合中选择两个集合各选一个。设合并的两个集合元素个数为a、b,则每次减去的数 = a * b * (在其他集合中选出2个元素,且这两个元素不在同一集合的方案数)。

求在其他集合中选出2个元素,且这两个元素不在同一集合的方案数,可以先求任选2个的方案数,再减去来自同一个集合的方案数。

因此,我们可以在进行每个操作时维护一个 tmp 值,tmp=\sum_{i=1}^{k}C_{size[i]}^2,(size[ i ]中是每个集合的元素数),记录当前状态下选择两个元素在同一集合中的方案数。

具体解释见代码。

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ll;

const ll maxn=100005;

ll fa[maxn],size[maxn];

//并查集的Get操作 
ll get(ll x){
	if(fa[x]==x)  return x;
	return fa[x]=get(fa[x]);
}

//并查集的Merge操作
void merge(ll x,ll y){
	ll fx=get(x),fy=get(y); 
	fa[fy]=fx;
	size[fx]=size[fx]+size[fy];
}

//计算每个集合中选择两个元素的方案数 
ll getCi2(ll num){
	if(num<2)  return 0;
	else  return num*(num-1)/2;
}

int main(){
	ll n,m;
	scanf("%lld%lld",&n,&m);
	ll x,y;
	//初始化 
	for(ll i=1;i<=n;i++){
		fa[i]=i;
		size[i]=1;
	}
	//n<4要单独判断 
	if(n<4){
		for(ll i=1;i<=(m+1);i++){
			printf("0\n"); 
		}
		return 0;
	}
	ll ans=n*(n-1)/2*(n-2)/3*(n-3)/4;//这里要注意不能按下行的方式写,否则中间结果会溢出unsigned long long 
//	ll ans=(n*(n-1)*(n-2)*(n-3))/(24);
	printf("%lld\n",ans); 
	ll tmp=0;//维护的tmp值 
	for(ll i=1;i<=m;i++){
		scanf("%lld%lld",&x,&y);
		ll fx=get(x),fy=get(y);
		if(fx==fy){//若两个人已经是朋友,不做操作 
			printf("%lld\n",ans);
			continue;
		}
		//减去当前两个集合下的方案数 
		tmp-=getCi2(size[fx]);
		tmp-=getCi2(size[fy]);
		ans-=size[fx]*size[fy]*((n-size[fx]-size[fy])*(n-size[fx]-size[fy]-1)/2-tmp);
		printf("%lld\n",ans);
		tmp+=getCi2(size[fx]+size[fy]);//更新tmp值 
		merge(x,y);//合并两个集合 
	}
	return 0;
} 
发布了30 篇原创文章 · 获赞 5 · 访问量 900

猜你喜欢

转载自blog.csdn.net/qq_42840665/article/details/99682386
今日推荐