1. 程式人生 > WINDOWS開發 >CF219D Choosing Capital for Treeland (樹形dp)

CF219D Choosing Capital for Treeland (樹形dp)

這道題顯然是一個樹上問題,題目讓我們求到各個點得逆序邊最小的點是哪些

我們對於樹形dp,一般來說都有一個邊上的權值,那麼對於本題,我們就要對題目資訊進行轉化

所以我們不妨把正向邊記作0,逆向邊記作1,這樣我們就能夠通過一次dfs來計算到子樹中的各個節點需要多少次逆轉

我們可以隨便挑1作為根節點

那麼剩下的問題也比較清晰,既然知道了子樹,只需知道往上的次數是多少,往上的次數無非就是父節點減去我這顆子樹的答案

這樣就是你的父親節點到其他點的次數了,相加就是答案

這裡還有一個小細節就是第二次dfs你和父親節點相連的這條邊的權值考慮

技術分享圖片
#include<bits/stdc++.h>
using
namespace std; typedef long long ll; const int N=4e5+10; const int mod=1e9+7; int f[N]; int ne[N],e[N],idx,w[N],h[N]; void add(int a,int b,int c){ e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; } void dfs1(int u,int fa){ int i; for(i=h[u];i!=-1;i=ne[i]){ int j=e[i]; if(j==fa)
continue; dfs1(j,u); f[u]+=f[j]+w[i]; } } void dfs2(int u,int fa){ int i; for(i=h[u];i!=-1;i=ne[i]){ int j=e[i]; if(j==fa) continue; f[j]+=(f[u]-f[j])+(w[i]?-1:1); dfs2(j,u); } } int main(){ int n; int i; cin>>n; memset(h,
-1,sizeof h); for(i=1;i<n;i++){ int a,b; scanf("%d%d",&a,&b); add(a,b,0); add(b,a,1); } dfs1(1,0); dfs2(1,0); int ans=n-1; for(i=1;i<=n;i++) ans=min(ans,f[i]); vector<int> num; for(i=1;i<=n;i++){ if(f[i]==ans) num.push_back(i); } cout<<ans<<endl; for(auto x:num){ printf("%d ",x); } cout<<endl; }
View Code