【题意】一个国家有n个点,由单向边连接,现在要改变一些点到其所能到达的所有点的方向,问怎么改最小,并且输出那些点
【做法】暂且把这道题分为树dp,把边方向转变成边权,正向为0,反向为1.经过转换,问题变成求某点为根到所有点的边权总和,求边权总和最小的那些点。所以,把这颗树看成有根树,这里选1为树根,一次dfs处理处树根的大小,然后再由树根更新树根的儿子结点。
【AC代码】
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200005;
const int INF = 0x3f3f3f3f;
struct Edge{
int v,w,next;
}E[maxn*2];
int head[maxn],tot;
void Init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void Add_Edge(int u,int v,int w)
{
E[tot].v=v,E[tot].w=w,E[tot].next=head[u],head[u]=tot++;
}
int dp[maxn],n;
void dfs(int u,int fa)//u = root! 1
{
dp[u] = 0;
for(int i=head[u]; ~i; i=E[i].next){
int v = E[i].v;
if(v!=fa)
{
dfs(v,u);
dp[u] += dp[v]+E[i].w;
}
}
}
void pushdown(int u,int fa)
{
for(int i=head[u]; ~i; i=E[i].next){
int v=E[i].v;
if(v!=fa)
{
dp[v] = dp[u];
if(E[i].w) dp[v]-=1;
else dp[v]++;
pushdown(v,u);
}
}
}
int main()
{
ios::sync_with_stdio(false);
int u,v;
cin>>n;
Init();
for(int i=1; i<n; i++){
cin>>u>>v;
Add_Edge(u,v,0);
Add_Edge(v,u,1);
}
dfs(1,-1);
pushdown(1,-1);
int ans = INF;
for(int i=1; i<=n; i++){
ans = min(ans,dp[i]);
}
cout<<ans<<endl;
for(int i=1; i<=n; i++){
if(dp[i]==ans)
cout<<i<<" ";
}
cout<<endl;
return 0;
}