zoukankan      html  css  js  c++  java
  • 牛客多校第九场 E All men are brothers 并查集/组合论

    题意:

    一开始有n人互不认识,每回合有两个人认识,认识具有传递性,也就是相互认识的人组成小团体。现在问你每个回合,挑选四个人,这四个人互不认识,有多少种挑选方法。

    题解:

    认识不认识用并查集维护即可,重点在于如何统计挑选方法。

    每个回合两个人互相认识,排除两个人本就在一个小团体中的情况,实际上就是两个小团体结合为一个。

    那么变得无效化的挑选方法,实际上就是两个人分别来自这两个小团体,剩下两个人来自其他小团体的情况。

    从其他集合的所有元素先任取两个,再去掉来自同一集合的两个的情况。

    减少的数量等于合并的两个集合大小乘以以上结果。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef unsigned long long uLL;
    typedef long long ll;
    typedef pair<int, LL>P;
    const int M = 4e5 * 4 + 5;
    const LL mod = 1e9 + 7;
    const LL lINF = 0x3f3f3f3f3f3f3f3f;
    #define ls (rt<<1)
    #define rs (rt<<1|1)
    int n, fa[M], ra[M], m;
    uLL num[M];
    void init(int n)
    {
        for (int i = 1; i <= n; i++)
        {
            fa[i] = i;
            ra[i] = 1;
            num[i] = 1;
        }
    }
    int find(int x)
    {
        if (fa[x] == x)
            return x;
        else
            return fa[x] = find(fa[x]);
    }
    void unite(int x, int y)
    {
        x = find(x);
        y = find(y);
        if (ra[x] < ra[y])
        {
            fa[x] = y;
            num[y] += num[x];
        }
        else
        {
            fa[y] = x;
            num[x] += num[y];
            if (ra[x] == ra[y])
                ra[x]++;
        }
    }
    uLL ans;
    uLL sum;
    int l, r;
    int main()
    {
        scanf("%d%d", &n, &m);
        ans = (uLL)n *(n - 1)*(n - 2)/6*(n - 3)/4;
        cout<<ans<<endl;
        sum = 0;
        init(n);
        while (m--)
        {
            scanf("%d%d", &l, &r);
            l = find(l);
            r = find(r);
            if (l == r)
            {
               cout<<ans<<endl;
                continue;
            }
            else
            {
                uLL lst = n - num[l] - num[r];
                uLL tmp;
                tmp = lst * (lst - 1) / 2;
                tmp = tmp - sum + num[l] * (num[l] - 1) / 2 + num[r] * (num[r] - 1) / 2;
                tmp = tmp * num[l] * num[r];
                ans -= tmp;
                cout<<ans<<endl;
                tmp = num[l] * (num[l] - 1) / 2 + num[r] * (num[r] - 1) / 2;
                sum -= tmp;
                unite(l, r);
                l = find(l);
    //            r = find(r);
    //            assert(l==r);
                sum += num[l] * (num[l] - 1) / 2;
            }
        }
    }
  • 相关阅读:
    leetcode 之 Palindrome Partitioning
    虚拟机共享文件夹下tar
    leetcode 之 Excel Sheet Column Number
    AndroidHttpClient & jsoup 解析 正方教务系统
    查看android下的分区表
    ubuntukylin 下编译 android4.4
    android:layout_weight 和 android: weightSum的使用
    Intent 与Bundle的传值关系
    使用自定义的Toast
    SlideMenu例子解析2
  • 原文地址:https://www.cnblogs.com/isakovsky/p/11361280.html
Copyright © 2011-2022 走看看