All men are brothers(并查集+思维 好题!!!)

链接: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.
在这里插入图片描述
官方题解如上。
这个题目解法不少,并查集+dp或者像题解这么做。个人认为题解这样算是比较好理解的。dp确实很简单,但是真的不好理解(dp算是一点不会吧)。
一开始我们能求出原始总共有多少种做法。合并两个集合之后,对原答案的贡献是多少?这是解题应该想的。
假设是第一次合并,合并两个集合之前,我们选四个人,可以直接c(n,4)来求解。
合并之后,减少了多少呢?在合并之前,我们可以在要合并的两个集合中各选出一个人,在从剩余的里面选出两个人,在剩余的里面选出两个人还需要减去在相同集合选出两个人的情况。这是这两个集合在合并前做的贡献,合并后就没有了。。那么我们在合并后就应该减去这一块值。这就是合并后的答案值。在剩余的人里面选出两个人,我们要减去相同集合里面选出两个人的情况,我们设一个变量sum记录一下,在一开始这个值为0,合并某两个集合后,对于sum来说,少了(size[t1](size[t1]-1)/2+(size[t2](size[t2]-1)/2))种情况(就是在这两个集合选出两个人的情况),但是多了这两个集合合并起来之后选出两个人的情况。这样想就很简单了。
代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxx=1e5+100;
int f[maxx];
ll size[maxx];
ll ans,sum;
ll n,m,k;

int getf(int u)
{
	return u==f[u]?u:f[u]=getf(f[u]);
}
void merge(int u,int v)
{
	int t1=getf(u);
	int t2=getf(v);
	if(t1!=t2)
	{
		ll sub=size[t1]+size[t2];//要合并的两个集合总数 
		sum-=(size[t1]*(size[t1]-1)/2+size[t2]*(size[t2]-1)/2);// 合并后对于sum减少了这些
		ll lose=size[t1]*size[t2]*((n-sub)*(n-sub-1)/2-sum);//合并两个集合对总数的贡献是减少了要合并的两个集合乘积再乘上剩下的人里面选出2个人 
		ans-=lose;
		sum+=sub*(sub-1)/2;//合并之后对sum增加了这些
		k--;//合并一次,集合数就减少一个,少于四个就直接输出零就行。
		f[t1]=t2;
		size[t2]+=size[t1];
	}
	printf("%lld\n",ans);
}
int main()
{
	int x,y;
	while(~scanf("%lld%lld",&n,&m))
	{
		for(int i=0;i<=n;i++) f[i]=i,size[i]=1;
		k=n;
		sum=ans=0;
		ans=n*(n-1)/2;
	    ans*=(n-2);
	    ans/=12;
	    ans*=(n-3);
		printf("%lld\n",ans);
		while(m--)
		{
			scanf("%d%d",&x,&y);
			if(k<4) printf("0\n");
			else merge(x,y);
		}
	}
	return 0;
}

努力加油a啊,(o)/~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

starlet_kiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值