首先抽象一个超级源点S,他向所有源点都有一条有向边。然后就是建立一棵树,使得这棵树满足对于每个节点,在有向可拓扑图中说删掉他而使得源点不可达的节点都是以他为根的这棵子树中的节点。
建立这棵树的做法就是先将原图拓扑排序一次,然后按照拓扑序依次处理每个点,由于拓扑序的原因,每当处理到一个点,当前点的父节点一定在当前已建的树上,然后当前点的所有食物在这棵树上的最近公共祖先就是当前点的父节点。
/** * Problem:catas * Author:Shun Yao * Time:2013.6.3 * Result:Accepted * Memo:graphs */ #include <cstring> #include <cstdio> void swap(long &x, long &y) { if (x == y) return; x ^= y; y ^= x; x ^= y; } const long Maxn = 65555; long n, d[Maxn], fa[Maxn], ans[Maxn], du[Maxn]; class gnode { public: long v; gnode *next; gnode() {} ~gnode() {} gnode(long V, gnode *ne):v(V), next(ne) {} } *g[Maxn], *G[Maxn], *gg[Maxn]; void addg(long x, long y) { g[x] = new gnode(y, g[x]); } void addG(long x, long y) { G[x] = new gnode(y, G[x]); } void addgg(long x, long y) { gg[x] = new gnode(y, gg[x]); } long get(long x, long y) { if (d[x] < d[y]) swap(x, y); while (d[x] > d[y]) x = fa[x]; while (x != y) { x = fa[x]; y = fa[y]; } return x; } long q[Maxn], *l, *r; int main() { static long i, a; static gnode *e; freopen("catas.in", "r", stdin); freopen("catas.out", "w", stdout); scanf("%ld", &n); for (i = 1; i <= n; ++i) { du[i] = 0; while (scanf("%ld", &a), a) { addg(i, a); addG(a, i); ++du[i]; } } r = l = q; for (i = 1; i <= n; ++i) if (du[i] == 0) *(r++) = i; while (l != r) { for (e = G[*l]; e; e = e->next) if (!--du[e->v]) *(r++) = e->v; ++l; } memset(d, 0, sizeof d); l = q; while (l != r) { if (g[*l]) { a = g[*l]->v; for (e = g[*l]->next; e; e = e->next) a = get(a, e->v); addgg(a, *l); fa[*l] = a; d[*l] = d[a] + 1; } else { addgg(0, *l); fa[*l] = 0; d[*l] = 1; } ++l; } l = r; do { --l; ans[*l] = 1; for (e = gg[*l]; e; e = e->next) ans[*l] += ans[e->v]; } while (l != q); printf("%ld", ans[1] - 1); for (i = 2; i <= n; ++i) printf("\n%ld", ans[i] - 1); fclose(stdin); fclose(stdout); return 0; }