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.

题目大意:现在给你N个人,给你M个关系,然后问你当第1~i条关系执行完后,找出4个人完全没关系的种类数

思路:这个题目当时比赛的时候没有想到他的那个怎么计算每一次的个数,没有做出来,下来以后看了大部分人的代码发现很多人都有着一样的公式,

我们现在推一下这个公式,比如现在有5个集合,a,b,c,d,e。

而且a和b是马上就要合并了的,那么我们现在的方案数到底减少了多少呢?

只看到减少的话,我么可以考虑a和b组成集合前的贡献,贡献也就是我们在a和b中选一个数,在其他的中选一个数,

那么就是 a*b(c*d+d*e+c*e),这也就是ab所做的贡献了,那么我们要把这个贡献去掉,就要把这个式子一般化

看了网上的大神的做法是这样的,

然后就是再化为一般话,令sum1为人数,sum2为人数和的平方和

然后就得到了这样的公式

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll  unsigned long long
using namespace std;
const int maxn=100000+10;
ll c[maxn],sum1,sum2;
int F[maxn];
ll n,m;
int findset(int x)
{
    if(F[x]==-1) return x;
    return F[x]=findset(F[x]);
}
int main()
{
    memset(F,-1,sizeof(F));
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        c[i]=1;
    ll ans=n*(n-1)/2*(n-2)/3*(n-3)/4;
    //cout<<" "<<ans<<endl;
    sum1=n,sum2=n;
    int x,y;
    for(int i=1;i<=m;i++)
    {
        cout<<ans<<endl;
        scanf("%d%d",&x,&y);
        int fx=findset(x),fy=findset(y);
        if(ans==0||fx==fy) continue;
        F[fx]=fy;
        ll a=c[fx],b=c[fy];
        ans-=a*b*((sum1-a-b)*(sum1-a-b)-(sum2-a*a-b*b))/2;
        sum2+=2*a*b;
        c[fy]+=c[fx];
    }
    cout<<ans<<endl;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值