题解:
可并堆优化$dp$。
由于$ans$只由$l$与派遣人数决定,我们可以贪心选取总和$<=m$的人。
有两种选择,一种是维护小根堆,一直$pop$到弹出的总和$>m$;
另一种是维护大根堆,一直$pop$到剩下总和$<=m$;
这两种比较一定是维护大根堆更优,因为每次$pop$后剩下的堆可以直接回溯。
然后$dp$就好了。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int N = 100050; template<typename T> inline void read(T&x) { T f = 1,c = 0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} x = f*c; } int n,b[N],c[N],hed[N],cnt,rt[N],dis[N],ls[N],rs[N],siz[N]; ll m,l[N],ans=0,sum[N]; struct EG { int to,nxt; }e[N]; void ae(int f,int t) { e[++cnt].to = t; e[cnt].nxt = hed[f]; hed[f] = cnt; } int merge(int x,int y) { if(!x||!y)return x+y; if(c[x]<c[y])swap(x,y); rs[x] = merge(rs[x],y); if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]); dis[x] = dis[rs[x]]+1; return x; } int pop(int x) { return merge(ls[x],rs[x]); } struct Pair { int x; ll w; Pair(){} Pair(int x,ll w):x(x),w(w){} friend bool operator < (Pair a,Pair b) { return a.w < b.w; } }; int cal(int u) { while(sum[u]>m) { sum[u]-=c[rt[u]]; rt[u]=pop(rt[u]); siz[u]--; } return siz[u]; } void dfs(int u) { siz[u]=1;sum[u]=c[u]; for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; dfs(to); siz[u]+=siz[to]; sum[u]+=sum[to]; rt[u] = merge(rt[u],rt[to]); } ans = max(ans,l[u]*cal(u)); } int main() { read(n),read(m); for(int i=1;i<=n;i++) { read(b[i]),read(c[i]),read(l[i]); ae(b[i],i); rt[i]=i,dis[i]=1; } dfs(1); printf("%lld ",ans); return 0; }