2020多校hdu第三场 1005:Little W and Contest (hdu6795)

  2020多校hdu第三场 1005:Little W and Contest (hdu6795)
  题目:http://acm.hdu.edu.cn/showproblem.php?pid=6795
  题目描述:
  每个人的权值为1或2,要求选3个人组成一队,队里的人权值总和>=5,且他们互不认识。
一开始他们都不认识,在之后n-1天里,每2个人认识,且认识关系具有传递性,即2个互相
不认识的人,若他们认识的人相互认识,则他们也会认识。问n天内,每天可以组成多少队。
  分析:
  显而易见是用并查集,考虑在x,y这两个人认识后对答案的影响。设互相认识这个朋友圈,
是一个集合,x在u集合,y在v集合中。因为u,v集合合并后,从u集合中选出的人和v中的人组队
就变成不合法的了,我们只要用前一天的答案减去这些不合法的组合就是这天的答案。

  在并查集中用按高度归并,就很容易得到他们合并后总的根。

  代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<sstream>
#include<vector>
#include<stack>
#include<deque>
#include<cmath>
#include<map>
#include<queue>
#include<bitset>
#define sd(x) scanf("%d",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
const int maxn=1e5+79;
const int mod=1e9+7;
const ll INF=1e18+7;
int a[maxn],fa[maxn];
int cnt1[maxn],cnt2[maxn],Rank[maxn];
int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
int unit(int x,int y)
{
    x=find(x),y=find(y);
    if(Rank[x]<=Rank[y])
    {
        cnt1[y]+=cnt1[x];
        cnt2[y]+=cnt2[x];
        fa[x]=y;
        if(Rank[x]==Rank[y]) Rank[y]++;
        return y;
    }
    else
    {
        cnt1[x]+=cnt1[y];
        cnt2[x]+=cnt2[y];
        fa[y]=x;
        return x;
    }
}
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        ms(cnt1,0);
        ms(cnt2,0);
        ms(Rank,0);
        int n;
        sd(n);
        ll num1=0,num2=0;
        fu(i,1,n)
        {
            fa[i]=i;
            sd(a[i]);
            if(a[i]==1) cnt1[i]=1,num1++;
            else cnt2[i]=1,num2++;
        }
        ll ans=num2*(num2-1)/2*num1%mod+num2*(num2-1)*(num2-2)/6%mod;
        ans%=mod;
        printf("%lld\n",ans);
        fu(i,1,n-1)
        {
            int x,y;
            sd(x);sd(y);
            int fx=find(x),fy=find(y);
            ll x1=cnt1[fx],x2=cnt2[fx];
            ll y1=cnt1[fy],y2=cnt2[fy];
            int rt=unit(x,y);//总的根
            //首先减去在集合x,y中选2个2的情况
            ans=(ans-(n-cnt1[rt]-cnt2[rt])*x2*y2%mod+mod)%mod;
            //然后是在集合x,y中选一个1的情况
            ans=(ans-(num2-cnt2[rt])*x1*y2%mod+mod)%mod;
            ans=(ans-(num2-cnt2[rt])*x2*y1%mod+mod)%mod;
            printf("%lld\n",ans);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/studyshare777/p/13397607.html
今日推荐