LuoguP6748 『MdOI R3』Fallen Lord 樹形DP+set
阿新 • • 發佈:2020-08-10
首先,肯定沒有不合法情況(每條邊的權值都賦值為 $1$ 就一定合法)
然後對於一條邊 $(x,y)$ 來說,只可能有 3 種取值.
1. 取 $a[x]$
2. 取 $a[y]$
3. 取 $m$
然後轉化成這一步後就可以進行樹形 DP 了.
令 $f[x][0],f[x][1]$ 分別表示以 $x$ 為根的子樹,且 $x$ 與 $x$ 父親連邊的邊權小於等於 $a[x]$/大於 $a[x]$ 的最大權和.
這個裸做的話是一個 $O(n^2)$ 的樹形揹包.
但是我們發現這個問題中所有物品的體積都是 $1$,那麼我們就可以先貪心選取一個兒子 $y$ 的最優決策點.
如果該決策點沒有讓邊權小於等於 $a[x]$ ,就把差量扔進一個 set 裡,貪心取出需要補齊的部分就行了.
程式碼:
#include <cstdio> #include <set> #include <vector> #include <cstring> #include <algorithm> #define N 500009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; const ll inf=1000000000; int n,m,edges; ll f[N][2]; int deg[N]; int a[N],hd[N],to[N<<1],nex[N<<1]; void add(int u,int v) { nex[++edges]=hd[u]; hd[u]=edges,to[edges]=v; } multiset<ll>se[N]; multiset<ll>::iterator it; void dfs(int x,int ff) { int det=deg[x]/2+1,cnt=0; // f[x][0] : det-1 // f[x][1] : det f[x][0]=f[x][1]=0; for(int i=hd[x];i;i=nex[i]) { int y=to[i]; if(y==ff) continue; dfs(y,x); ll cur=max(f[y][0]+a[y],f[y][1]+m); f[x][0]+=cur; f[x][1]+=cur; if(a[x]>=a[y]) { if(f[y][1]+m==cur&&m>a[x]) { se[x].insert(-(max(f[y][0]+a[y],f[y][1]+a[x])-cur)); } else ++cnt; } else { se[x].insert(-(max(f[y][0]+a[x],f[y][1]+a[x])-cur)); } } int k0=det-1,k1=det; if(cnt+se[x].size()>=k0) { it=se[x].begin(); for(int i=1;i<=k0-cnt;++i) { f[x][0]-=(*it); it++; } } else f[x][0]=-inf; if(cnt+se[x].size()>=k1) { it=se[x].begin(); for(int i=1;i<=k1-cnt;++i) { f[x][1]-=(*it); it++; } } else f[x][1]=-inf; se[x].clear(); if(deg[x]==1&&ff) { f[x][0]=0; f[x][1]=-inf; } } int main() { // setIO("input"); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); a[i]=min(a[i],m); } int x,y,z; for(int i=1;i<n;++i) { scanf("%d%d",&x,&y); add(x,y); add(y,x); ++deg[x]; ++deg[y]; } dfs(1,0); printf("%lld\n",f[1][1]); return 0; }