D. Choosing Capital for Treeland
http://codeforces.com/problemset/problem/219/D
1 /* 2 CodeForces D. Choosing Capital for Treeland (转化为树形DP) 3 题意:给一个n节点的有向无环图,要找一个这样的点: 4 该点到其它n-1要逆转的道路最少,(边<u,v>,如果v要到u去,则要逆转该边方向) 5 如果有多个这样的点,则升序输出所有 6 7 思路:把边的方向化为权值,正向为1,逆向为0。 8 问题转化为找哪些点的在遍历全图后总权值最大。 9 这就是树形DP了,考虑每个节点,它可以从子树收获价值,也可以从父亲收获。 10 所以dfs两遍,一边把子树的价值存到dps[i]里,再一遍把父亲的价值存到dpf[i]里。 11 ans[i] = dps[i] + dpf[i]。 12 13 */ 14 15 #include<cstdio> 16 #include<algorithm> 17 #include<cstring> 18 19 using namespace std; 20 21 const int MAXN = 200010; 22 const int MAXM = 500010; 23 24 struct Edge{ 25 int to,nxt,w; 26 }e[MAXM]; 27 struct ANS{ 28 int id,v; 29 bool operator < (const ANS &a) const { 30 if (v==a.v) return id < a.id; 31 return v > a.v; 32 } 33 }ans[MAXN]; 34 int head[MAXM],tot; 35 int dps[MAXN],dpf[MAXN]; 36 37 38 inline int read() { 39 int x = 0,f = 1;char ch = getchar(); 40 for (; ch<'0'||ch>'9'; ch = getchar()) 41 if (ch=='-') f = -1; 42 for (; ch>='0'&&ch<='9'; ch = getchar()) 43 x = x*10+ch-'0'; 44 return x*f; 45 } 46 47 inline void add_edge(int u,int v,int w) { 48 e[++tot].to = v,e[tot].w = w,e[tot].nxt = head[u],head[u] = tot; 49 } 50 51 void dfs1(int u,int fa) { 52 for (int i=head[u]; i; i=e[i].nxt) { 53 int v = e[i].to,w = e[i].w; 54 if (v==fa) continue; 55 dfs1(v,u); // 叶 -> 根 56 dps[u] += dps[v]+w; 57 } 58 } 59 void dfs2(int u,int fa) { 60 for (int i=head[u]; i; i=e[i].nxt) { 61 int v = e[i].to,w = e[i].w; 62 if (v==fa) continue; 63 dpf[v] += (w?0:1)+dpf[u]+dps[u]-dps[v]-w; 64 dfs2(v,u); //根 -> 叶 65 } 66 } 67 68 int main() { 69 70 int n = read(); 71 for (int u,v,i=1; i<n; ++i) { 72 u = read(),v = read(); 73 add_edge(u,v,1),add_edge(v,u,0); 74 } 75 dfs1(1,0); 76 dfs2(1,0); 77 78 for (int i=1; i<=n; ++i) { 79 ans[i].v = dps[i]+dpf[i]; 80 ans[i].id = i; 81 } 82 sort(ans+1,ans+n+1); 83 84 int sum = n-1-ans[1].v,cnt = 1; 85 for (int i=2; i<=n; ++i) 86 if (ans[i].v==ans[1].v) cnt++; 87 else break; 88 89 printf("%d\n",sum); 90 for (int i=1; i<=cnt; ++i) { 91 printf("%d ",ans[i].id); 92 } 93 return 0; 94 }