http://blog.csdn.net/acm_ted/article/details/7922132
题意:给你一个有向的树,让你选择一个点使得通过反向一些边让这点能到所以其他的点,同时让需要反向的边最少。
题解:两次树形DP,第一次求子树需要反向的边,第二次累加父树以上需要反向的边,取和的最小值即为答案。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAX 200005
int edge[MAX<<1];//表示第i条边的终点
int next[MAX<<1];//与第i条边同起点的下一条边的位置
int head[MAX<<1];//以i为起点的第一条边的储存位置
int to[MAX<<1];
int dp[MAX],dpx[MAX];
void insert(int i,int a,int b,int c)//a起点,b终点
{
edge[i]=b;
next[i]=head[a];
to[i]=c;
head[a]=i;
}
void dfs_f(int x,int p)//统计子树的修改次数
{
dp[x]=0;
for(int i=head[x];i!=-1;i=next[i])
{
int k=edge[i];
if(k==p) continue;
dfs_f(k,x);
dp[x]+=(dp[k]+(to[i]==x));
}
}
void dfs_m(int x,int p,int idx)
{
if(p==-1) dpx[x]=dp[x];
else
{
if(to[idx]==x) dpx[x]=dpx[p]+1;
else dpx[x]=dpx[p]-1;
}
for(int i=head[x];i!=-1;i=next[i])
{
int k=edge[i];
if(k==p) continue;
dfs_m(k,x,i);
}
}
int main()
{
memset(head,-1,sizeof(head));
memset(next,-1,sizeof(next));
int n,a,b;
scanf("%d",&n);
for(int i=1,j=1;i<n;++i)
{
scanf("%d%d",&a,&b);
insert(j++,a,b,b);
insert(j++,b,a,b);
}
dfs_f(1,-1);
dfs_m(1,-1,1);
int maxx=0xfffff;
for(int i=1;i<=n;++i)
if(dpx[i]<maxx) maxx=dpx[i];
printf("%d\n",maxx);
for(int i=1;i<=n;++i)
if(dpx[i]==maxx)
printf("%d ",i);
return 0;
}