題解 洛谷 P4189 【[CTSC2010]星際旅行】
阿新 • • 發佈:2020-07-21
一個比較直接的想法就是對每個點進行拆點,拆成入點和出點,限制放在入點和出點相連的邊上,然後跑最大費用最大流即可。
但是這樣複雜度無法接受,所以考慮模擬費用流來解決本題。
發現 \(H\) 都大於等於該節點的度數,所以從根節點出發,一定可以到達所有節點。
先考慮以根節點為起點和終點的答案,首先可以遍歷整棵樹後回到根節點,每條邊的兩個端點的 \(H\) 都減一,答案加二。然後繼續考慮每條邊的貢獻,若其兩個端點在遍歷後 \(H\) 不為 \(0\),則可以反覆走這條邊,直到其中一個端點的 \(H\) 為 \(0\)。
得出根節點的答案後,考慮如何推出其他點的答案。設當前節點為 \(x\),當前考慮的兒子為 \(y\)
同時還要注意在 \(dfs\) 時需要回溯。
#include<bits/stdc++.h> #define maxn 100010 using namespace std; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int n,tot; int v[maxn],ans[maxn],son[maxn]; struct edge { int to,nxt; }e[maxn]; int head[maxn],edge_cnt; void add(int from,int to) { e[++edge_cnt]=(edge){to,head[from]}; head[from]=edge_cnt; } void dfs_pre(int x,int fa) { for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to,val; if(y==fa) continue; dfs_pre(y,x),val=min(v[x],v[y]); v[x]-=val,v[y]-=val,tot+=val*2; if(v[y]) son[x]=y; } } void dfs_ans(int x,int fa) { ans[x]=tot; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to,type; if(y==fa) continue; if(v[x]) v[x]--,tot++,type=1; else if(son[y]) v[son[y]]--,tot++,type=2; else v[y]++,tot--,type=3; dfs_ans(y,x); if(type==1) v[x]++,tot--; if(type==2) v[son[y]]++,tot--; if(type==3) v[y]--,tot++; } } int main() { read(n); for(int i=1;i<=n;++i) read(v[i]); for(int i=1;i<n;++i) { int x,y; read(x),read(y),x++,y++; add(x,y),add(y,x),v[x]--,v[y]--,tot+=2; } dfs_pre(1,0),dfs_ans(1,0); for(int i=1;i<=n;++i) printf("%d\n",ans[i]); return 0; }