【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;
}
相關文章
相關標籤/搜索