http://codeforces.com/problemset/problem/219/D
题意
给一颗树但边是单向边,求至少旋转多少条单向边的方向,可以使得树上有一点可以到达树上任意一点,若有多个答案按顺序全部输出。
分析
把边的方向化为权值,正向为0,逆向为1。问题转化为找哪些点的在遍历全图后总权值最小。这就是树形DP了,它可以从子树收获价值,也可以从父亲收获。所以dfs两遍。
定义dp[u][0]为以u为根的的子树全可达的修改次数,随便选定一个点,用子节点信息更新父节点,dp[u][0]+=dp[v][0]+w;
再定义dp[u][1]为以u为根的整颗树需要修改的次数,根据第一次dp的结果,利用父节点的信息来更新子节点。
如果fa指向u,那么dp[u][1]=dp[fa][1]-1;如果u指向fa,那么dp[u][1]=dp[fa][1]+1;
#include<iostream> #include<cmath> #include<cstring> #include<queue> #include<vector> #include<cstdio> #include<algorithm> #include<map> #include<set> #define rep(i,e) for(int i=0;i<(e);i++) #define rep1(i,e) for(int i=1;i<=(e);i++) #define repx(i,x,e) for(int i=(x);i<=(e);i++) #define X first #define Y second #define PB push_back #define MP make_pair #define mset(var,val) memset(var,val,sizeof(var)) #define scd(a) scanf("%d",&a) #define scdd(a,b) scanf("%d%d",&a,&b) #define scddd(a,b,c) scanf("%d%d%d",&a,&b,&c) #define pd(a) printf("%d ",a) #define scl(a) scanf("%lld",&a) #define scll(a,b) scanf("%lld%lld",&a,&b) #define sclll(a,b,c) scanf("%lld%lld%lld",&a,&b,&c) #define IOS ios::sync_with_stdio(false);cin.tie(0) using namespace std; typedef long long ll; template <class T> void test(T a){cout<<a<<endl;} template <class T,class T2> void test(T a,T2 b){cout<<a<<" "<<b<<endl;} template <class T,class T2,class T3> void test(T a,T2 b,T3 c){cout<<a<<" "<<b<<" "<<c<<endl;} const int N = 1e6+10; //const int MAXN = 210; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3fll; const ll mod = 200907; int T; void testcase(){ printf("Case #%d: ",++T); } const int MAXN = 2e5+5; const int MAXM = 30; struct node{ int to,w,nxt; }edge[MAXN<<1]; int cnt,head[MAXN]; int dp[MAXN][2]; void init(){ cnt=0; mset(head,-1); } void addEdge(int u,int v,int w){ edge[cnt].to=v; edge[cnt].w=w; edge[cnt].nxt=head[u]; head[u]=cnt++; } void sear(int u,int pa){ dp[u][0]=0; for(int i=head[u];~i;i=edge[i].nxt){ int v=edge[i].to; if(v!=pa){ sear(v,u); dp[u][0]+=(dp[v][0]+edge[i].w); } } } void dfs(int u,int pa,int x){ if(u==pa){ dp[u][1]=dp[u][0]; }else{ if(x==0) dp[u][1]=dp[pa][1]+1;//pa->u else dp[u][1]=dp[pa][1]-1;//u->pa } for(int i=head[u];~i;i=edge[i].nxt){ int v=edge[i].to; if(v!=pa){ dfs(v,u,edge[i].w); } } } int main() { #ifdef LOCAL freopen("in.txt","r",stdin); #endif // LOCAL int n; scd(n); int u,v; init(); for(int i=1;i<n;i++){ scdd(u,v); addEdge(u,v,0); addEdge(v,u,1); } sear(1,-1); // for(int i=1;i<=n;i++) printf("%d ",dp[i][0]);puts(""); dfs(1,1,0); int ans=inf; for(int i=1;i<=n;i++) ans=min(ans,dp[i][1]); cout<<ans<<endl; for(int i=1;i<=n;i++){ if(ans==dp[i][1]){ printf("%d ",i); } } return 0; }