http://codeforces.com/problemset/problem/219/D
题意:
给出一树的节点n和有向边n-1条,
对每个点有个信息:该点到所有其他点,如果遇到一条反向边,计数器+1,
求出,最后这个计数器最小的点,如果有多个,增序输出
题目本身是无根树,为了方便我们直接看成一个以1为根的有根树即可:
显然是一个树DP,
我们把正向边权值val为0,反向边权值为1
第一次dfs,自底向上求出每个节点的到所有叶子节点所遇到的反向边数之和 dn[x]
然后对每个节点,要求题目所要求的信息,我们还缺【该节点到根节点的反向边数量,和 到兄弟节点的反向边数量】
到根结点的好求,我们只需要再来个dfs2,这次自上向下,每到达一个节点,给up[x]加上从根到当前节点的反向边数量。
然后对于到兄弟节点的反向边的话需要利用父亲节点的信息,就等于 dn[x]-dn[v]-(!val) 【val是边权】
#include <cstdio> #include <cmath> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <set> #include <vector> #include <iostream> using namespace std; const double pi=acos(-1.0); double eps=0.000001; int min(int a,int b) {return a<b?a:b;} struct node { int x,v,id; node(int a=0,int b=0,int c=0) {x=a,v=b;id=c;} }; vector<node> mp[200005]; int up[200005]; int dn[200005]; int vis[200005]; int ans[200005]; int ok=0; void dfs2(int x ) { vis[x]=1; int i; for (i=0;i<mp[x].size();i++) { int v=mp[x][i].x; int val=mp[x][i].v; val=!val; if (vis[v])continue; up[v]=val+dn[x]-dn[v]-(!val)+up[x]; dfs2(v); } } int dfs1(int x ) { int i;vis[x]=1; for (i=0;i<mp[x].size();i++) { int v=mp[x][i].x; int val=mp[x][i].v; if (vis[v])continue; dn[x]+=val; dn[x]+=dfs1(v); } return dn[x]; } int main() { int n; cin>>n; int i,j; int x,y; for (i=1;i<n;i++) { scanf("%d%d",&x,&y); mp[x].push_back(node(y,0,i)); mp[y].push_back(node(x,1,i)); } dfs1(1); memset(vis,0,sizeof(vis)); dfs2(1); int minn=1e9,maxi=-1; for (i=1;i<=n;i++) minn=min(minn,up[i]+dn[i]); for (i=1;i<=n;i++) { if (minn==up[i]+dn[i]) ans[++ok]=i; } printf("%d\n",minn); for (i=1;i<=ok;i++) printf("%d ",ans[i]); printf("\n"); return 0; }