【bzoj2809】[Apio2012]dispatching (左偏树)
我们需要枚举根,然后从其子树内选尽量多的点,薪水不超过M,可是暴力复杂度不对。
于是考虑自下而上合并树(开始每棵树内只有一个节点,就是自己)
每个树是一个堆,我们维护树的节点个数和薪水总和,合并时,不断弹出堆顶薪水最大的直到薪水总和不超过M,然后用领导力*节点个数更新答案。
发现这个模型就是裸的左偏树。
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> using namespace std; typedef long long LL; #define N 100010 int l[N],r[N],fa[N]; int tree[N],siz[N],d[N]; LL ld[N],c[N],sum[N]; int n; LL m,ans; int merge(int x,int y) { if (!x) return y; if (!y) return x; if (c[x]<c[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; sum[x]=sum[l[x]]+sum[r[x]]+c[x]; siz[x]=siz[l[x]]+siz[r[x]]+1; return x; } int main() { scanf("%d%lld",&n,&m); for (int i=1;i<=n;i++) { scanf("%d%lld%lld",&fa[i],&c[i],&ld[i]); tree[i]=i; siz[i]=1; sum[i]=c[i]; } for (int i=n;i>=1;i--) { while (sum[tree[i]]>m) tree[i]=merge(l[tree[i]],r[tree[i]]); ans=max(ans,siz[tree[i]]*ld[i]); if (i!=1) tree[fa[i]]=merge(tree[fa[i]],tree[i]); } printf("%lld",ans); return 0; }