码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-07-29 21:59:33      阅读:95      评论:0      收藏:0      [点我收藏+]

标签:基础   efi   int   pen   view   lap   std   自身   地址   

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=6795

题意:有n个人,分为两类,一类为权值为1的人,一类为权值为2的人。起初,这n个人互不相识,然后又n-1此介绍,每次介绍u与v相识(在这之前u,v并不认识,若a认识b,b认识c,则a也认为认识c),在第i次介绍之前输出当前的组合方案。组合是从挑选3个人,3个人的权值和不小于5,并且3个人互不相识

输入:第一行一个整数t,表示测试样例的个数

对于每个测试样例,第一行一个整数n,第二行n个整数(1或2),表示权值,接下来n-1行表示n-1次介绍

输出:对于每个测试样例,在第i次介绍之前,输出当前的组合的方案,如果输比较大,则输出对10^9+7的模

题解:对于3个人的组合权值不小于5只有两种方式:2+2+2或者2+2+1.

看到里面的a认识b,b认识c则a认识c,便会意识到这肯定是并查集存储关系。

然后,观察其中的数学逻辑,对于组合方案的数目(不取模的话)整体而言是非递增排列,我们可以先计算出初始的组合数目sum,对于每一次的介绍只要在上一次的sum的基础上减去因为当前介绍而不能组队的方案数目即可。另外,题目里面说u,v并不相识,说明在此之前u,v属于两个不同的集合,我们只需要标记每个集合含有的1的数目和2的数目,每次sum减去p2[ru]*p2[rv]*(now2+now1)+p1[ru]*p2[rv]*now2+p2[ru]*p1[rv]*now2.其中now1,now2表示除当前两个集合之外的其余的1与2的数目的总和就可以了。

AC代码

技术图片
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long int
const int N=100000+5;
const int maxn=1000000000+7; 
ll p1[N],p2[N];//分别存储集合的祖宗为i的集合的1和2的数量 
ll s1,s2;//记录1和2的总数 
ll sum;//初始时(还没有第一次介绍前的组队数) 
int fa[N];//并查集的记录数组 
int my[N];//表示自身节点是2或者是1 
int n;
void init(){
    s1=0,s2=0;
    for(int i=1;i<=n;i++){
        fa[i]=i;//并查集数组初始化 
        if(my[i]==1) p1[i]=1,p2[i]=0,s1++;
        else p1[i]=0,p2[i]=1,s2++;
    }
    sum=s2*(s2-1)*(s2-2)/2/3+s2*(s2-1)/2*s1;
    sum%=maxn;
}
int find(int x){
    if(x==fa[x]) return x;
    else return fa[x]=find(fa[x]);
}
void merge(int u,int v){
    int ru=find(u),rv=find(v);
    fa[rv]=ru;
    ll now1=s1-p1[ru]-p1[rv],now2=s2-p2[ru]-p2[rv];
    sum=sum-p2[ru]*p2[rv]*(now2+now1)-p1[ru]*p2[rv]*now2-p2[ru]*p1[rv]*now2;
    while(sum<0) sum+=maxn;//之前这里写的是sum+=maxn然后就一直出错,因为sum加了一次maxn之后还有可能是负的 
    sum%=maxn;
    p1[ru]+=p1[rv];
    p2[ru]+=p2[rv];
}
int main(){
    int t;cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++) scanf("%d",&my[i]);
        init();
        cout<<sum<<endl;
        int u,v;
        for(int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            merge(u,v);
            cout<<sum<<endl;
        }
    } 
    return 0;
} 
View Code

 

写于:2020/7/29  19:34

 

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

标签:基础   efi   int   pen   view   lap   std   自身   地址   

原文地址:https://www.cnblogs.com/sunjianzhao/p/13399171.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!