[BZOJ2809][Apio2012]dispatching(左偏树)
首先对于一个节点以及它的子树,它的最优方案显然是子树下选最小的几个
用左偏树维护出每棵子树最优方案的节点,记录答案
然后它的这棵树可以向上转移给父节点,将所有子节点的左偏树合并再维护就是父节点的最优方案
这个过程中维护答案即可
Code
#include <cstdio> #include <algorithm> #define ll long long #define N 100010 using namespace std; struct info{int to,nex;}e[N*2]; int n,m,tot,head[N],c[N],l[N],rt[N],sz[N],cnt; ll Ans,sum[N]; namespace Lt{ int cnt,l[N],r[N],v[N],d[N]; int merge(int x,int y){ if(!x||!y) return x+y; if(v[x]<v[y]) swap(x,y); r[x]=merge(r[x],y); if(d[r[x]]>d[l[x]]) swap(l[x],r[x]); d[x]=d[r[x]]+1; return x; } inline int tp(int x){return v[x];} inline void pop(int &x){x=merge(l[x],r[x]);} } inline void Link(int u,int v){ e[++tot].to=v;e[tot].nex=head[u];head[u]=tot; } inline int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void solve(int u){ rt[u]=++cnt; Lt::v[cnt]=c[u]; sz[u]=1,sum[u]=c[u]; for(int i=head[u],v;i;i=e[i].nex){ solve(v=e[i].to); sz[u]+=sz[v]; sum[u]+=sum[v]; rt[u]=Lt::merge(rt[u],rt[v]); } for(;sum[u]>m;){ sum[u]-=Lt::tp(rt[u]),Lt::pop(rt[u]); sz[u]--; } Ans=max(Ans,sz[u]*1ll*l[u]); } int main(){ n=read(),m=read(); for(int i=1;i<=n;++i) Link(read(),i),c[i]=read(),l[i]=read(); solve(1); printf("%lld\n",Ans); return 0; }