1. 程式人生 > 實用技巧 >Luogu P3574 [POI2014]FAR-FarmCraft

Luogu P3574 [POI2014]FAR-FarmCraft

題目

可以發現子樹最優保證全域性最優這個性質,所以猜測樹形DP

令f[u]表示以u為根的子樹的最小時間,轉移似乎要O(n!)列舉轉移順序,感覺需要貪心嘗試

令g[u]表示以u為根的子樹路上所消耗的時間,則f[v]-g[v]表示憑白消耗的時間,按照排隊接水的方式貪心,以f[v]-g[v]從大到小的順序轉移

f[u]=1+f[v]+g[u],g[u]+=g[v]+2

注意:dfs中全域性變數不能呼叫多次

#include<bits/stdc++.h>
using namespace std;

const int N=5e5+5;
int cnt,to[N<<1],nxt[N<<1
],he[N],n,c[N],f[N],g[N]; struct A{ int u,s; }; bool operator <(A i,A j) { return i.s<j.s; } priority_queue<A>q; inline void add(int u,int v) { to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt; } void dfs(int fa,int u) { for(int e=he[u];e;e=nxt[e]) { int v=to[e]; if(v!=fa) dfs(u,v); }
for(int e=he[u];e;e=nxt[e]) { int v=to[e]; if(v!=fa) q.push((A){v,f[v]-g[v]}); } f[u]=c[u]; while(!q.empty()) { int v=q.top().u; q.pop(); f[u]=max(f[u],f[v]+g[u]+1); g[u]+=g[v]+2; } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf(
"%d",&c[i]); } for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(0,1); printf("%d\n",max(f[1],c[1]+g[1])); return 0; }