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;
}