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.

题目大意:
先输入两个整数 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=ansCA1CB1CC2
而对于 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=numCA1CC1CB1CC1CA1CB1,但还需记住每次合并后需将 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+B1CC1

代码:

//#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值