zoukankan      html  css  js  c++  java
  • hdu 6795 Little W and Contest 并查集+排列组合

    题意:

    t组输入,有n个人,刚开始谁也不认识谁。每一个人有一个权值w[i](1<=w[i]<=2),你要挑选3个互相不认识的人组成一个队,且要保证3个人权值之和大于等于5(也就意味着最少要有权值为2的人)

    为你能找到多少个满足题意得队伍

    然后给你n-1个关系,每个关系输入x y

    这表示x和y认识了,而且如果z认识y,那么x也认识z。即关系具有传递性

    然后在输出你还能找到多少个满足题意得队伍(这样循环n-1次)

    题解:

    因为关系有传递性,所以如果x和y认识,我们可以让x所在这个集合和y所在这个集合合并成一个集合,也就是这里用到了并查集

    我们开始统计一下权值为1得总人数为cnt1,权值为2得总人数为cnt2

    那么最开始n个人都不认识的时候能找到sum个满足题意的队伍,下面算式用排列组合表示:Ccnt23+C2cnt2*cnt1

    sum=cnt2*(cnt2-1)*(cnt2-2)/6+cnt2*(cnt2-1)*cnt1/2;

    用s1[i]表示第i个集合有s1[i]个人的权值为1

    用s2[i]表示第i个集合有s2[i]个人的权值为2

    如果x 合并入 y 集合,那么要重新统计一下s1[y]和s2[y],而且一些队伍不能满足题意了,要减去不满足题意得队伍

    代码:

    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <vector>
    #include <map>
    #include <queue>
    #include <stack>
    #include <set>
    #include <ctime>
    #include <cstring>
    #include <cstdlib>
    #include <math.h>
    #include<map>
    using namespace std;
    typedef long long ll;
    const int MOD=1e9+7;
    const int maxn = 1e5 + 5;
    ll v[maxn],w[maxn];
    ll n,s[maxn],s2[maxn],cnt1,cnt2,sum;
    ll finds(ll x)
    {
        if(v[x]!=x)
        {
            ll y=finds(v[x]);
            v[x]=y;
            return y;
        }
        return x;
    }
    void solve(ll x,ll y)
    {
        ll fx=finds(x);
        ll fy=finds(y);
    
        sum=sum-(s2[fx]*s2[fy]*(cnt2-s2[fx]-s2[fy]));  // 2 2 2,意思:从s2[fx]中选择一个2权值的人,从s2[fy]中选择一个2权值的人,从剩下不再s2[fx]和s2[fy]集合里面,且权值为2的人中选择一个
        sum=sum-(s2[fx]*s2[fy]*(cnt1-s[fx]-s[fy]));    // 2 2 1
        sum=sum-(s2[fx]*s[fy]*(cnt2-s2[fx]-s2[fy]));  // 2 1 2
        sum=sum-(s[fx]*s2[fy]*(cnt2-s2[fx]-s2[fy]));   // 1 2 2
    
        v[fx]=fy;
        s[fy]+=s[fx];
        s2[fy]+=s2[fx];
        printf("%lld
    ",sum%MOD);
    }
    int main()
    {
        ll t;
        scanf("%lld",&t);
        while(t--)
        {
            cnt1=cnt2=sum=0;
            memset(s,0,sizeof(s));
            memset(s2,0,sizeof(s2));
            scanf("%lld",&n);
            for(ll i=1;i<=n;++i)
            {
                v[i]=i;
                scanf("%lld",&w[i]);
                if(w[i]==1) cnt1++,s[i]++;
                else cnt2++,s2[i]++;
            }
            sum=cnt2*(cnt2-1)*(cnt2-2)/6+cnt2*(cnt2-1)*cnt1/2;
            printf("%lld
    ",sum%MOD);
            for(ll i=1;i<n;++i)
            {
                ll x,y;
                scanf("%lld%lld",&x,&y);
                solve(x,y);
            }
    
        }
        return 0;
    }
  • 相关阅读:
    20170809上课笔记
    20170808上课笔记
    20170807上课笔记
    20170804上课笔记
    《备份恢复3》
    《备份恢复2》
    《SQL语句测试》
    《备份恢复1》
    《oracle管理7》
    《oracle管理6》
  • 原文地址:https://www.cnblogs.com/kongbursi-2292702937/p/13418501.html
Copyright © 2011-2022 走看看