zoukankan      html  css  js  c++  java
  • 【AGC016E】Poor Turkeys

    Description

      
      有(n)(1 le n le 400))只鸡,接下来按顺序进行(m)(1 le m le 10^5))次操作。每次操作涉及两只鸡,如果都存在则随意拿走一只;如果只有一只存在,拿走这一只;如果都不存在,什么都不做。
      
      求最后有多少对鸡(无序)可能共同存活。
      
      
      

    Solution

      
      个人认为单用集合的解释方法有失偏颇。
      
      首先考虑枚举两只鸡,规定它们一定要存活,然后模拟过程。怎么看单次模拟的复杂度都不会小于(m),因此要第一时间舍弃这种方法。
      
      于是要换个角度考虑。我们看看能不能算出某一只鸡存活的条件,再枚举两只鸡,并判断它们的条件是否冲突。
      
      假设我们令(a)必须存活。
      
      先看那些与(a)有关的操作:显然,另一只鸡在该操作前必须存活。我们虽然得到了这个结论,但是这些操作的顺序有先后影响,并不好考虑。
      
      为了消除后效性,我们从后往前考虑每个事件。如果遇到与(a)有关的事件((a,b)),我们必须令(b)在这个时刻前存活。这意味着下次遇到与(a)(b)有关的事件,我们必须令另一者在这个时刻前存活。我们记如果"(a)必须存活",当前所有必须存活的鸡组成的集合为(S),则形式化地讲:
      
      初始时,(S)里只有(a)
      
      1.如果遇到一个事件,其中一者属于(S),则另一者必须在这个时刻前存活。我们将另一者加进(S)
      
      2.如果两者都属于(S),则必须死一个。这立刻违反了(S)的定义,因此(a)不可能存活。我们将其纳入统计答案的考虑对象
      
      3.如果两者都不属于(S),由于我们从后往前考虑,即使这两者在更早的时间与(a)的生死有关,但那个有关的时刻结束之后,这两者的生死并不重要。因此这个事件不需要纳入考虑范围。
      
      由此,扫完全部事件之后,依赖存活关系可以形象为一棵内向树(上述1.发生时,从另一者向属于(S)的一者连一条有向边),我们不再将其看做集合考虑,因为那无法解释接下来的事情。我们称它为(a)的存活树。
      
      (a)的存活树的每一条边都代表着一次依赖事件,每一次事件的成功与否都决定了(a)能否存活。事件发生的具体顺序我们不需要知道,但是一定是按照从叶子节点向上的某个拓扑序发生的。
      
      考虑两只鸡(a)(b)能否存活。有了存活树的概念,却无从下手?先从简单的一面看:如果二者的存活树的点集无交,那么显然没有影响,二者可以共存。关键是如果有交,可以共存吗?
      
      对于一个点(x),其在(a)(b)的存活树中都出现。如果(x)在两棵树中的父亲不同,这代表着两次不同的事件,先后发生,却都依赖于(x)。则后发生的一者必然不能保证(x)存活,因此(a)(b)有一个必须死。如果(x)在两棵树中的父亲相同,首先二者不可能是两个事件,不然二者自身都不可能存活,不在考虑范围之内;既然是同一个事件,那么它们在这一步的确共存,因为它们共同进行了有益的一步。我们会发现,两棵树中可能出现一些“共同链”,但这并不意味着二者可以共存。因为两棵树的根一定不同,所以“共同链”的链顶一定不是根,即“共同链”的链顶一定会出现第一个情况:父亲不一样,有一只鸡必须死。
      
      由上证毕,两只鸡能共存,当且仅当存活树的点集无交集。
      
      在实现时,不需要建树,树只是用来严格证明的,我们只需要计算出每只存活的鸡的存活树点集合即可。
      
        
      

    Code

      

    #include <cstdio>
    #include <bitset>
    using namespace std;
    const int N=405,M=10005;
    typedef bitset<N> bs400;
    int n,m;
    int a[M][2];
    bool die[N];
    bs400 b[N];
    void readData(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    		scanf("%d%d",&a[i][0],&a[i][1]);
    }
    void calc(){
    	for(int i=1;i<=n;i++){
    		b[i][i]=1;
    		for(int j=m;j>=1;j--){
    			int u=a[j][0],v=a[j][1];
    			if(b[i][u]&&b[i][v]){
    				die[i]=true;
    				break;
    			}
    			else if(b[i][u])
    				b[i][v]=1;
    			else if(b[i][v])
    				b[i][u]=1;
    		}
    	}
    }
    void print(){
    	int ans=0;
    	for(int i=1;i<n;i++)
    		if(!die[i])
    			for(int j=i+1;j<=n;j++)
    				if(!die[j]){
    					if((b[i]&b[j]).none())
    						ans++;
    				}
    	printf("%d
    ",ans);
    }
    int main(){
    	readData();
    	calc();	
    	print();
    	return 0;
    }
    
  • 相关阅读:
    全基因组关联分析学习资料(GWAS tutorial)
    GWAS研究可利用的数据库(20200424更新)
    本周最新文献速递20200614
    本周最新文献速递20200607
    甲基化数据QC: 使用甲基化数据推测SNP基因型(ewastools工具)
    文献速递20200531
    查找感兴趣的基因、基因组区域是否有调控元件的在线网页工具EpiRegio
    许嵩
    甲基化数据QC:使用甲基化数据计算样本间的相关性
    there is no package called 'GO.db'报错解决方案
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/9552950.html
Copyright © 2011-2022 走看看