2019牛客暑期多校训练营(第九场)E All men are brothers (并查集+组合数)

6 篇文章 0 订阅
4 篇文章 0 订阅

题目链接:https://ac.nowcoder.com/acm/contest/889/E

题意:有n个人,开始互不是朋友,有m次操作,每次使得两人是朋友,(朋友具有传递性)。

问:从所有人中选出4个人,这四个人两两不是朋友的选择方案有多少种?对于每次操作输出一次

思路:现场做的时候虽然也是用的并查集和组合数,但是就是一直超时,可能是常数太大了吧,加上各种优化之后还是T到结束

赛后题解一处,还是优化不够到家啊,

考虑每次合并带来的贡献,

设一个集合人数为x,另一个为y,剩下的为z,合并之后为xy,

合并时,总的方法数会

1、减去x选1个,z选3个

2、减去y选1个,z选3个

3、减去x选1个,y选1个,z选2个

合并之后总的方法数会

4、加上xy选一个,z选3个

可以发现1,2    和 4,是抵消的,所以只考虑3即可

为了减少除法运算,可以维护一个选2个的组合数,用+-来维护

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 10;
ll f[maxn], siz[maxn];
int getf(int x){
    return x == f[x] ? x : f[x] = getf(f[x]);
}
__int128 sum, pre;
void merg1e(int x, int y) {
    f[x] = y;
    siz[y] += siz[x];
}
int main(){
    ll n, q;
    scanf("%lld %lld", &n, &q);
    for(int i = 0; i <= n; i++) f[i] = i, siz[i] = 1;
    sum = (__int128)n * (n - 1) * (n - 2) * (n - 3) / 24;
    pre = n * (n - 1) / 2;
    printf("%lld\n", sum);
    while(q-- ){
        int l, r;
        scanf("%d %d", &l, &r);
        int x = getf(l);
        int y = getf(r);
        if(x == y) printf("%lld\n",sum);
        else {
            ll a = siz[x], b = siz[y];
            ll num = n - a - b;
            ll tmp = pre - a * num - b * num - a * b;
            sum -= a * b * tmp;
            pre = tmp + (a + b) * num;
            printf("%lld\n", sum);
            merg1e(x, y);
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值