BZOJ2815:[ZJOI2012]災難(拓撲排序,LCA)


Description

       阿米巴是小強的好朋友。

       阿米巴和小強在草原上捉螞蚱。小強突然想,如果螞蚱被他們捉滅絕了,那么吃螞蚱的小鳥就會餓死,而捕食小鳥的猛禽也會跟着滅絕,從而引發一系列的生態災難。

       學過生物的阿米巴告訴小強,草原是一個極其穩定的生態系統。如果螞蚱滅絕了,小鳥照樣可以吃別的蟲子,所以一個物種的滅絕並不一定會引發重大的災難。

       我們現在從專業一點的角度來看這個問題。我們用一種叫做食物網的有向圖來描述生物之間的關系:

       一個食物網有N個點,代表N種生物,如果生物x可以吃生物y,那么從y向x連一個有向邊。

       這個圖沒有環。

       圖中有一些點沒有連出邊,這些點代表的生物都是生產者,可以通過光合作用來生存; 而有連出邊的點代表的都是消費者,它們必須通過吃其他生物來生存。

       如果某個消費者的所有食物都滅絕了,它會跟着滅絕。

       我們定義一個生物在食物網中的“災難值”為,如果它突然滅絕,那么會跟着一起滅絕的生物的種數。

       舉個例子:在一個草場上,生物之間的關系是:

       如果小強和阿米巴把草原上所有的羊都給嚇死了,那么狼會因為沒有食物而滅絕,而小強和阿米巴可以通過吃牛、牛可以通過吃草來生存下去。所以,羊的災難值是1。但是,如果草突然滅絕,那么整個草原上的5種生物都無法幸免,所以,草的災難值是4。

       給定一個食物網,你要求出每個生物的災難值。

Input

        輸入文件 catas.in 的第一行是一個正整數 N,表示生物的種數。生物從 1 標

號到 N。

       接下來 N 行,每行描述了一個生物可以吃的其他生物的列表,格式為用空

格隔開的若干個數字,每個數字表示一種生物的標號,最后一個數字是 0 表示列

表的結束。

Output

       輸出文件catas.out包含N行,每行一個整數,表示每個生物的災難值。

Sample Input

4 5
1 2 3
1 4 5
2 4 7
2 3 6
3 4 8

Sample Output

3 6

Solution

感謝zhhe0101學長orz的耐心講解
我太菜了聽了好久才懂
不過這個題的做法的確是十分精妙的
大體分三步:1、拓撲排序2、建樹3、求樹的前綴和
1、我們知道,一個生物死亡的條件是他的所有食物全部死亡
   而且既然這個題說了沒有環,那么就可以保證食物鏈等級鮮明
   那么求這種圖,我們很容易就可以想到拓撲排序了
2、我們建一棵樹,讓father[x]為x的父親,意味着若father[x]死了,那么x則滅絕
   問題來了,x應該掛在哪個點上?
   當然是他所有食物的LCA上啊!
   若LCA死了,x的食物也會都死亡,那么x也必然死亡
     建樹這里可以將出度為0的點連在一個虛擬點n+1上方便處理
3、最后DFS求一下樹的前綴和
     因為若x死了,那么他的子樹會全部死亡,那么他的重要度就是子樹大小-1(去除本身)
最后附樣例轉樹的圖(學長給我畫的)
5 →2 →1
    ↗  ↗
4 →3

        6
         ↓
        1
      ↙↓↘
     2  3  4
 ↙
5

Code

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<queue>
  4 #define MAXN (65534+5)
  5 using namespace std;
  6 int head1[MAXN],head[MAXN],num1,num2;
  7 int n,ind[MAXN],sum,topo[MAXN];
  8 int father[MAXN],f[MAXN][17],depth[MAXN];
  9 int SUM[MAXN];
 10 
 11 //1為拓撲序所用鄰接表 
 12 //2為樹所用鄰接表
 13 struct node1
 14 {
 15     int to,next;
 16 }edge1[MAXN*4];//這里不是很懂為什么要開MAXN*4……? 
 17 void add1(int u,int v)
 18 {
 19     edge1[++num1].to=v;
 20     edge1[num1].next=head1[u];
 21     head1[u]=num1;
 22 }
 23 struct node
 24 {
 25     int to,next;
 26 }edge[MAXN*2+10];
 27 void add(int u,int v)
 28 {
 29     edge[++num2].to=v;
 30     edge[num2].next=head[u];
 31     head[u]=num2;
 32 }
 33 
 34 //toposort拓撲排序 
 35 //RMQ倍增做LCA的預處理
 36 //LCA找兩個點的最近公共祖先
 37 //build將拓撲排序轉換為一顆有根樹 
 38 //DPtree求樹上前綴和 
 39 void toposort()
 40 {
 41     queue<int>q;
 42     for (int i=1;i<=n;++i)
 43         if(ind[i]==0)
 44             q.push(i);
 45             
 46     while (!q.empty())
 47     {
 48         int x=q.front();
 49         q.pop();
 50         topo[++sum]=x;
 51         for (int i=head1[x];i!=0;i=edge1[i].next)
 52         {
 53             ind[edge1[i].to]--;
 54             if(!ind[edge1[i].to]) 
 55                 q.push(edge1[i].to);
 56         }
 57     }    
 58 }
 59 void RMQ(int x)
 60 {
 61     f[x][0]=father[x];
 62     for (int i=1;i<=16;++i)
 63         f[x][i]=f[f[x][i-1]][i-1];
 64 }
 65 int LCA(int x,int y)//x is under y 
 66 {
 67     if (depth[x]<depth[y])    swap(x,y);
 68     for (int i=16;i>=0;--i)
 69         if (f[x][i]!=0&&depth[f[x][i]]>=depth[y])
 70             x=f[x][i];
 71     if (x==y)    return y;
 72     for (int i=16;i>=0;--i)
 73         if (f[x][i]!=0&&f[y][i]!=0&&f[x][i]!=f[y][i])
 74         {
 75             x=f[x][i];
 76             y=f[y][i];
 77         }
 78     return father[x];
 79 }
 80 void build()
 81 {
 82     depth[n+1]=1;
 83     father[n+1]=n+1;
 84     for (int i=n;i>=1;--i)
 85     {
 86         int x=topo[i];
 87         if (head1[x]==0)
 88         {
 89             father[x]=n+1;
 90             add(n+1,x); 
 91             f[x][0]=n+1;
 92             depth[x]=2;
 93             continue;
 94         }
 95         int lca=edge1[head1[x]].to;
 96         for (int i=edge1[head1[x]].next;i!=0;i=edge1[i].next)
 97         {
 98             lca=LCA(lca,edge1[i].to);
 99         }
100         father[x]=lca;
101         add(father[x],x); 
102         depth[x]=depth[father[x]]+1;
103         RMQ(x);
104     }
105 }
106 void DPtree(int x)
107 {
108     SUM[x]=1;
109     for (int i=head[x];i!=0;i=edge[i].next)
110     {
111         DPtree(edge[i].to);
112         SUM[x]+=SUM[edge[i].to];
113     }
114 }
115 
116 //main函數 
117 int main()
118 {
119     int x;
120     scanf("%d",&n);
121     for (int i=1;i<=n;++i)
122     {
123         scanf("%d",&x);
124         while (x!=0)
125         {
126             add1(i,x);
127             ++ind[x];//食物入度+1 
128             scanf("%d",&x);
129         }
130     }
131     toposort();//拓撲排序
132     build();//建樹
133     DPtree(n+1); 
134     for (int i=1;i<=n;++i)
135         printf("%d\n",SUM[i]-1);
136 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM