【题意】一个国家有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;
}