BZOJ2809 [Apio2012]dispatching 可並堆
阿新 • • 發佈:2017-12-12
style 操作 isp eap 超過 add 傳送門 int 什麽
歡迎訪問~原文出處——博客園-zhouzhendong
去博客園看該題解
題目傳送門 - BZOJ2809
題意概括
n個點組成一棵樹,每個點都有一個領導力和費用,可以讓一個點當領導,然後在這個點的子樹中選擇一些費用之和不超過m的點,得到領導的領導力乘選擇的點的個數(領導可不被選擇)的利潤。求利潤最大值。n≤100000
題解
做一個類似樹形dp的操作。
維護大根堆,每次從子節點到父節點就是合並所有的子節點的堆。
利用左偏樹。
然後先刪掉大的,直到合法為止。
好像沒什麽要講的。
代碼
#include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; const int N=100005; struct Gragh{ int cnt,y[N],nxt[N],fst[N]; void clear(){ cnt=0; memset(fst,0,sizeof fst); } void add(int a,int b){ y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt; } }g; int n,m,L[N],fa[N],C[N],root[N],cnt[N]; struct heap{ int ls,rs,v,len; void set(int a,int b,int c,int d){ ls=a,rs=b,v=c,len=d; } }h[N]; LL ans=0,tot[N]; LL max(LL a,LL b){ return a>b?a:b; } int merge(int a,int b){ if (a==0||b==0) return a+b; if (h[a].v<h[b].v) swap(a,b); h[a].rs=merge(h[a].rs,b); if (h[h[a].ls].len<h[h[a].rs].len) swap(h[a].ls,h[a].rs); h[a].len=h[h[a].rs].len+1; return a; } void dfs(int rt){ tot[rt]=C[rt],cnt[rt]=1; root[rt]=rt; h[rt].set(0,0,C[rt],0); for (int i=g.fst[rt];i;i=g.nxt[i]){ dfs(g.y[i]); root[rt]=merge(root[rt],root[g.y[i]]); tot[rt]+=tot[g.y[i]],cnt[rt]+=cnt[g.y[i]]; } while (tot[rt]>m){ tot[rt]-=h[root[rt]].v; root[rt]=merge(h[root[rt]].ls,h[root[rt]].rs); cnt[rt]--; } ans=max(ans,1LL*L[rt]*cnt[rt]); } int main(){ scanf("%d%d",&n,&m); g.clear(); for (int i=1;i<=n;i++){ scanf("%d%d%d",&fa[i],&C[i],&L[i]); g.add(fa[i],i); } dfs(1); printf("%lld",ans); return 0; }
BZOJ2809 [Apio2012]dispatching 可並堆