2019牛客多校第九场——E. 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.

输入

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.

题意:

每次给你一队朋友关系,问当前取四个人互相不是朋友的取法种类数,朋友关系有传递性

分析:

开始是\textrm{C}_{n}^{4},然后每一次并查集,判断(并修改),减掉不行的

显然就是当前要合并的两个朋友群各出一个,剩下的任出一个x,从不是x的朋友群里再抓一个;

kk是x可取的人数;sum记录的是截至上一个朋友关系,被合并的朋友群各选一个人的种类数总和;

所以有kk*kk-sum,还要再加上自己选自己,因为这样也是不合理的

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int maxn=1e5+7;
ull num[maxn];
int pre[maxn];
ull ans,n,sum;
int m;
int find(int x) {return pre[x]==x?x:pre[x]=find(pre[x]);}
void merge(int x,int y)
{
	int fx=find(x),fy=find(y);
	if(fx!=fy) 
	{
		ull kk=n-num[fx]-num[fy];
		ull tmp=num[fx]*num[fy]*(kk*kk-sum+num[fx]*num[fx]+num[fy]*num[fy])/2;
		if(ans>tmp) ans-=tmp;
		else ans=0;
		sum+=2*num[fx]*num[fy];
		pre[fy]=fx;num[fx]+=num[fy];num[fy]=0;
	}
}
int main()
{
	scanf("%llu%d",&n,&m);
	for (int i=0;i<=(int)n;i++) pre[i]=i,num[i]=1;
	ans=n*(n-1)/2*(n-2)/3*(n-3)/4;
	sum=n;
	while (m--)
	{
		printf("%llu\n",ans);
		int u,v;scanf("%d%d",&u,&v);
		if(ans==0) continue;
		merge(u,v);
	}
	printf("%llu\n",ans);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值