zoukankan      html  css  js  c++  java
  • 2020hdu多校第三场1005(6795)Little W and Contest

    题目地址: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


    作者:孙建钊
    出处:http://www.cnblogs.com/sunjianzhao/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    Leetcode463. Island Perimeter
    C++ 编写的解码器小程序 map
    LeetCode706. Design HashMap
    LeetCode705. Design HashSet
    LeetCode804. Unique Morse Code Words
    c++后台开发 准备材料
    Scott Young-《如何高效学习》
    跳表和散列
    时间复杂度 log n
    第35题:LeetCode138. Copy List with Random Pointer
  • 原文地址:https://www.cnblogs.com/sunjianzhao/p/13399171.html
Copyright © 2011-2022 走看看