BZOJ4910 : [Sdoi2017] 苹果树
问题等价于树形依赖背包,允许一条链每个点各免费一次。
设f[i][j]f[i][j]表示按DFS序考虑到ii,体积为jj的最大收益。
先放入不能免费的物品,等遍历完儿子后再放入必选的物品,那么ii到根路径上所有点都只算了不能免费的部分。
然后将DFS序翻转,设h[i][j]h[i][j]表示按DFS序考虑到ii,体积为jj的最大收益。
等遍历完儿子后再放入必选的物品和不能免费的物品,那么ii到根路径上所有点都没有算。
如此一来,对于每个叶子ii,用f[i][j]+h[i][k−j]f[i][j]+h[i][k−j]更新答案即可。
对于不能免费的物品,需要用单调队列优化转移。
时间复杂度O(nk)O(nk)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #include<cstdio> #include<cstring> const int N=20010,M=25520010; int Case,n,m,K,T,i,fa[N],a[N],b[N],g[N],nxt[N],ans,F[M],H[M]; inline void add( int x, int y){nxt[y]=g[x];g[x]=y;} inline void up( int &a, int b){a<b?(a=b):0;} inline void solve( int *f, int A, int B){ int i,j=0,h=1,t=0; static int q[500010],p[500010]; for (i=0;i<=m;i++,j+=B){ f[i]-=j; while (h<=t&&f[q[t]]<f[i])t--; q[++t]=i; while (i-q[h]>A)h++; p[i]=f[q[h]]+j; } memcpy (f,p,T); } void dfsl( int x){ int i,j,A=a[x],B=b[x]; if (A)solve(F+x*K,A,B); for (i=g[x];i;i=nxt[i]){ memcpy (F+i*K,F+x*K,T); dfsl(i); B=b[i]; int *s=F+x*K+1,*e=F+i*K; for (j=1;j<=m;j++,s++,e++)up(*s,*e+B); } } void dfsr( int x, int y){ int i,j,A=a[x],B=b[x]; y+=B; for (i=g[x];i;i=nxt[i]){ memcpy (H+i*K,H+x*K,T); dfsr(i,y); B=b[i]; int *s=H+x*K+1,*e=H+i*K; for (j=1;j<=m;j++,s++,e++)up(*s,*e+B); } if (!g[x]){ int *s=H+x*K+m,*e=F+x*K; for (j=0;j<=m;j++,s--,e++)up(ans,*s+*e+y); } if (A)solve(H+x*K,A,b[x]); } int main(){ scanf ( "%d" ,&Case); while (Case--){ scanf ( "%d%d" ,&n,&m); K=m+1;T=K* sizeof ( int ); for (i=1;i<=n;i++)g[i]=0; for (i=1;i<=n;i++){ scanf ( "%d%d%d" ,&fa[i],&a[i],&b[i]);a[i]--; if (fa[i])add(fa[i],i); } memset (F+K,0,T); memset (H+K,0,T); dfsl(1); for (i=1;i<=n;i++)g[i]=0; for (i=n;i;i--) if (fa[i])add(fa[i],i); ans=0; dfsr(1,0); printf ( "%d\n" ,ans); } return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步