• 2019牛客暑期多校训练营(第九场) E All men are brothers


    这道题比赛的时候想到了正确解法,但是依然没敢做,怕超时,其实就是道简单并查集+组合计算。

    题解:

      1.首先要考虑的就是关系的传递,这就说明了我们可以把都不认识的人,分成几个集合。

      2.剩下就是集合的合并,由于我们需要计算组合数,所以需要把集合人数也记录一下。

      3.需要解释的估计就是这一条了 ans -= a * b * (pre - a * (n - a) - b * (n - b) + a * b); 建议先看代码

       目前ans的值为a和b未合并的情况下的值,但是目前a和b已经合并了,那么我们就需要减去a和b所贡献的组合数

       那么a和b贡献的组合数就是   : 从a中取1人,再从b中取1人,再从剩下的集合中挑取2人

       代码就是  a*b*(剩下集合中挑取两个的组合数)

       代码中说过pre是   ”取2个不同的人的取法”

       那么剩下集合中挑取两个的组合数就是 :pre - a * (n - a) - b * (n - b) + a * b 

       为什么要加上 a*b呢,因为 (n-a)中包括了b,(n-b)中包括了a,其实我们减去了两次 a*b,所以需要加回来一次。

      

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef double dou;
    typedef  long long ll;
    typedef pair<int, int> pii;
    typedef map<int, int> mii;
    
    #define pai acos(-1.0)
    #define M 100005
    #define inf 0x3f3f3f3f
    #define mod 1000000007
    #define IN inline
    #define left k<<1
    #define right k<<1|1
    #define lson L, mid, left
    #define rson mid + 1, R, right
    #define W(a) while(a)
    #define lowbit(a) a&(-a)
    #define ms(a,b) memset(a,b,sizeof(a))
    #define Abs(a) (a ^ (a >> 31)) - (a >> 31)
    #define false_stdio ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
    
    ll n, m;
    ll ans, pre, a, b, fa, fb;
    ll boss[M], num[M];
    
    int find(int k) {
        if (boss[k] == k)return k;
        else {
            boss[k] = find(boss[k]);
            return boss[k];
        }
    }
    
    int main() {
        false_stdio;
        cin >> n >> m;
        pre = n * (n - 1) / 2;//这个是在n个人中取2个不同的人的取法
        ans = n / 2 * (n - 1) / 3 * (n - 2) / 4 * (n - 3);//初始的最大值为C(4)(n)
    
        for (int i = 1; i <= n; i++) {
            //一开始都不认识,所以每个集合都是自己,人数也只有1
            num[i] = 1;
            boss[i] = i;
        }
        for (int i = 1; i <= m; i++) {
            cout << ans << endl;
            cin >> a >> b;
            fa = find(a), fb = find(b);//查找根节点
            if (fa == fb || ans == 0)continue;//如果根节点相同,也就是一个集合的,或者答案为0不能再减少了,那么就不用计算了
    
            a = num[fa], b = num[fb];//a为fa所在集合的人数,b为fb所在集合的人数
            //看不懂下面这条的可以回去看解释3了
            ans -= a * b * (pre - a * (n - a) - b * (n - b) + a * b);
            pre -= a * b;//减去a和b的组合数
    
            boss[fb] = fa;
            num[fa] += num[fb];
        }
        cout << ans << endl;
        return 0;
    }

       

      

  • 相关阅读:
    asp.net一些学习文档
    如何让baidu,google,yahoo搜索到自己的网页?
    [转] 驾校考试秘笈 不用看书就能通过!! 暂时不考也先留着 别等着急的时候没处找
    C++辅助工具 ZZ
    C/C++性能优化 ZZ
    Optimizing C and C++ Code ZZ
    《中国2030》报告解读创新兼谈研发经费 转载
    春三月,此谓发陈,天地俱生,万物以荣ZZ
    字符串翻转/反转(reverse a CStyle String)
    经典数字信号处理图书的个人评述ZZ
  • 原文地址:https://www.cnblogs.com/caibingxu/p/11366348.html
Copyright © 2020-2023  润新知