Description
阿米巴是小強的好朋友。
阿米巴和小強在草原上捉螞蚱。小強突然想,如果螞蚱被他們捉滅絕了,那么吃螞蚱的小鳥就會餓死,而捕食小鳥的猛禽也會跟着滅絕,從而引發一系列的生態災難。
學過生物的阿米巴告訴小強,草原是一個極其穩定的生態系統。如果螞蚱滅絕了,小鳥照樣可以吃別的蟲子,所以一個物種的滅絕並不一定會引發重大的災難。
我們現在從專業一點的角度來看這個問題。我們用一種叫做食物網的有向圖來描述生物之間的關系:
一個食物網有N個點,代表N種生物,如果生物x可以吃生物y,那么從y向x連一個有向邊。
這個圖沒有環。
圖中有一些點沒有連出邊,這些點代表的生物都是生產者,可以通過光合作用來生存; 而有連出邊的點代表的都是消費者,它們必須通過吃其他生物來生存。
如果某個消費者的所有食物都滅絕了,它會跟着滅絕。
我們定義一個生物在食物網中的“災難值”為,如果它突然滅絕,那么會跟着一起滅絕的生物的種數。
舉個例子:在一個草場上,生物之間的關系是:
如
如果小強和阿米巴把草原上所有的羊都給嚇死了,那么狼會因為沒有食物而滅絕,而小強和阿米巴可以通過吃牛、牛可以通過吃草來生存下去。所以,羊的災難值是1。但是,如果草突然滅絕,那么整個草原上的5種生物都無法幸免,所以,草的災難值是4。
給定一個食物網,你要求出每個生物的災難值。
Input
輸入文件 catas.in 的第一行是一個正整數 N,表示生物的種數。生物從 1 標
號到 N。
接下來 N 行,每行描述了一個生物可以吃的其他生物的列表,格式為用空
格隔開的若干個數字,每個數字表示一種生物的標號,最后一個數字是 0 表示列
表的結束。
Output
輸出文件catas.out包含N行,每行一個整數,表示每個生物的災難值。
Sample Input
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 }