ZJOI 2012 DAY2 灾难

contents

  1. 1. Problem
  2. 2. Input
  3. 3. Output
  4. 4. Sample Input
  5. 5. Sample Output
  6. 6. Solution

Problem

阿米巴是小强的好朋友。
阿米巴和小强在草原上捉蚂蚱。小强突然想,如果蚂蚱被他们捉灭绝了,那么吃蚂蚱的小鸟就会饿死,而捕食小鸟的猛禽也会跟着灭绝,从而引发一系列的生态灾难。
学过生物的阿米巴告诉小强,草原是一个极其稳定的生态系统。如果蚂蚱灭绝了,小鸟照样可以吃别的虫子,所以一个物种的灭绝并不一定会引发重大的灾难。
我们现在从专业一点的角度来看这个问题。我们用一种叫做食物网的有向图来描述生物之间的关系:
一个食物网有N个点,代表N种生物,如果生物x可以吃生物y,那么从y向x连一个有向边。
这个图没有环。
图中有一些点没有连出边,这些点代表的生物都是生产者,可以通过光合作用来生存; 而有连出边的点代表的都是消费者,它们必须通过吃其他生物来生存。
如果某个消费者的所有食物都灭绝了,它会跟着灭绝。
我们定义一个生物在食物网中的“灾难值”为,如果它突然灭绝,那么会跟着一起灭绝的生物的种数。
举个例子:在一个草场上,生物之间的关系是:

如果小强和阿米巴把草原上所有的羊都给吓死了,那么狼会因为没有食物而灭绝,而小强和阿米巴可以通过吃牛、牛可以通过吃草来生存下去。所以,羊的灾难值是1。但是,如果草突然灭绝,那么整个草原上的5种生物都无法幸免,所以,草的灾难值是4。
给定一个食物网,你要求出每个生物的灾难值。

Input

第一行是一个正整数N,表示生物的种数。生物从1标号到N。
接下来N行,每行描述了一个生物可以吃的其他生物的列表,格式为用空格隔开的若干个数字,每个数字表示一种生物的标号,最后一个数字是0表示列表的结束。

Output

包含N行,每行一个整数,表示每个生物的灾难值。

Sample Input

1
2
3
4
5
6
5
0
1 0
1 0
2 3 0
2 0

Sample Output

1
2
3
4
5
4
1
0
0
0

Solution

解法參照網路各位大神,效率為 O(E log V),必須為 DAG 圖。

题目大意 :给你一个有向可拓扑图,入度为零的点成为源点,然后考虑每一个点,假若删掉了他以及对应的边,那么除他外有多少个点不可达源点。

算法 :按拓扑序遍历N个点,将距每个点最近的必须通过的点设为它的父亲,构造出一棵树。对于每个点,若向它连边的点只有一个,其父亲就是向它连边的点;若向它连边的点不只一个,其父亲是向它连边的所有点的最近公共祖先。删掉每个点后不可达点个数就是其子树的节点个数。

這個做法接近貪婪思路,建造一個瓶頸樹,建造的過程找到最鄰近的瓶頸,而瓶頸的找法根據 LCA 完成。

  • 建造完,利用 dfs 計算子樹大小,發生 stackoverflow,系統恰好沒有抓到,改用 bfs 計算就 A 過。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <stdio.h>
#include <vector>
#include <queue>
#include <string.h>
#include <algorithm>
#include <assert.h>
using namespace std;
// http://oi.nks.edu.cn/showproblem?problem_id=2646
#define MAXN 65536
#define LOGMAXN 17
vector<int> g[MAXN], invg[MAXN], tree[MAXN];
int indeg[MAXN] = {};
vector<int> toposort(vector<int> g[], int n) {
vector<int> ret;
queue<int> Q;
int u, v;
for (int i = 0; i <= n; i++)
indeg[i] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < g[i].size(); j++)
indeg[g[i][j]]++;
}
for (int i = 1; i <= n; i++) {
if (indeg[i] == 0)
Q.push(i);
}
while (!Q.empty()) {
u = Q.front(), Q.pop();
ret.push_back(u);
for (int i = 0; i < g[u].size(); i++) {
v = g[u][i];
if (--indeg[v] == 0)
Q.push(v);
}
}
return ret;
}
int depth[MAXN], parent[MAXN][LOGMAXN];
int getLCA(int x, int y) {
int dist, i;
if (depth[x] < depth[y]) swap(x, y);
dist = depth[x] - depth[y];
for (int i = 0; dist; i++, dist /= 2) {
if (dist&1) x = parent[x][i];
}
for (i = 0; x != y;) {
if (parent[x][i] != parent[y][i] || (i == 0 && parent[x][i] == parent[y][i])) {
x = parent[x][i];
y = parent[y][i];
i++;
} else {
i--;
}
}
return x;
}
void buildTree(vector<int> &topo, vector<int> g[], int n) {
for (int i = 1; i <= n; i++)
tree[i].clear();
int u, v;
parent[0][0] = -1, depth[0] = 0;
for (int i = 0; i < topo.size(); i++) {
u = topo[i];
if (g[u].size() == 0) {
depth[u] = 1, parent[u][0] = 0;
continue;
}
int lca = g[u][0];
for (int j = 0; j < g[u].size(); j++)
v = g[u][j], lca = getLCA(lca, v);
depth[u] = depth[lca] + 1;
parent[u][0] = lca;
tree[lca].push_back(u);
for (int i = 0; parent[u][i]; i++) {
parent[u][i+1] = parent[parent[u][i]][i];
}
}
}
int used[MAXN], subtree[MAXN];
void sumTree(int n) {
queue<int> Q;
int u, v;
for (int i = 1; i <= n; i++) {
subtree[i] = 1;
indeg[i] = tree[i].size();
if (tree[i].size() == 0) Q.push(i);
}
while (!Q.empty()) {
u = Q.front(), Q.pop();
v = parent[u][0];
indeg[v]--;
subtree[v] += subtree[u];
if (indeg[v] == 0)
Q.push(v);
}
}
int main() {
int n, x, y, u, v;
while(scanf("%d", &n) == 1) {
for (int i = 0; i <= n; i++)
g[i].clear(), invg[i].clear();
for (int i = 1; i <= n; i++) {
while (scanf("%d", &x) == 1 && x) {
g[i].push_back(x);
invg[x].push_back(i);
}
}
vector<int> topo = toposort(invg, n);
buildTree(topo, g, n);
sumTree(n);
for (int i = 1; i <= n; i++)
printf("%d\n", subtree[i] - 1);
}
return 0;
}