【AGC016E】Poor Turkeys

Description

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

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

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\n",ans);
}
int main(){
    readData();
    calc(); 
    print();
    return 0;
}
相关文章
相关标签/搜索