[APIO2012]派遣 可并堆(左偏树)
没啥说的,自底向上合并大根堆即可.
一边合并,一边贪心弹堆顶直到堆的总和不大于预算.
Code:
#include <cstdio> #include <algorithm> #include <cstring> #define setIO(s) freopen(s".in","r",stdin) #define maxn 100000 + 5 #define ll long long using namespace std; int head[maxn],to[maxn],nex[maxn],cnt,root; int n; int siz[maxn],ch[maxn][2],dis[maxn]; ll m,lead[maxn],cost[maxn],ans,sumv[maxn],val[maxn]; void addedge(int u,int v) { nex[++cnt] = head[u],head[u] = cnt,to[cnt] = v; } int merge(int a,int b){ if(!a || !b) return a + b; if(val[a] < val[b]) swap(a,b); ch[a][1] = merge(ch[a][1],b); if(dis[ch[a][1]] > dis[ch[a][0]]) swap(ch[a][1],ch[a][0]); dis[a] = dis[ch[a][0]] + 1; sumv[a] = val[a] + sumv[ch[a][0]] + sumv[ch[a][1]]; siz[a] = siz[ch[a][0]] + siz[ch[a][1]] + 1; return a; } void pop(int &a){ int t = merge(ch[a][0],ch[a][1]); siz[a] = sumv[a] = val[a] = ch[a][0] = ch[a][1] = 0; a = t; } int dfs(int u){ int rt = u; val[rt] = cost[u]; siz[rt] = 1; sumv[rt] = val[rt]; for(int i = head[u]; i ; i = nex[i]){ rt = merge(rt,dfs(to[i])); while(sumv[rt] > m) pop(rt); } //printf("%d %d\n",u,siz[rt]); ans = max(ans,siz[rt] * lead[u]); return rt; } int main(){ //setIO("input"); scanf("%d%lld",&n,&m); for(int i = 1;i <= n;++i) { int a; scanf("%d%lld%lld",&a,&cost[i],&lead[i]); if(a == 0) root = i; else addedge(a,i); } dfs(root); printf("%lld",ans); return 0; }