bzoj 4182: Shopping 树形依赖背包+dsu on tree

题意

马上就是小苗的生日了,为了给小苗准备礼物,小葱兴冲冲地来到了商店街。商店街有n个商店,并且它们之间的道路构成了一颗树的形状。
第i个商店只卖第i种物品,小苗对于这种物品的喜爱度是wi,物品的价格为ci,物品的库存是di。但是商店街有一项奇怪的规定:如果在商店u,v买了东西,并且有一个商店w在u到v的路径上,那么必须要在商店w买东西。小葱身上有m元钱,他想要尽量让小苗开心,所以他希望最大化小苗买到物品的喜爱度之和。这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为OI选手的你,你能帮帮他吗?
N<=500,M<=4000,T<=5,Wi<=4000,Di<=100

分析

显然我们买东西的节点必然构成一棵连通子树,考虑最暴力的树形dp,我们有两种做法:
一种是设f[i,j]表示以节点i为根的子树且必选i用了j元的最大价值,然后每次把儿子所有合并上来,复杂度是O(nm^2)
一种是枚举每个节点作为根,然后作树形依赖背包,复杂度是O(n^2m)。
发现这两种算法都不能通过本题。
注意到第一种算法每次合并两个背包的复杂度是O(m^2)的,但这显然很不优秀因为一棵子树的节点数量是O(n)且在背包中加入一个点的复杂度是O(m)。也就是说我们每次合并背包还不如把整颗子树的节点加入到某个背包中。
这时想到了什么?dsu on tree!
加入节点的时候用单调队列优化一下,那么时间复杂度就是O(nmlogn)。
这题还可以用点分治做,就是每次从分治中心为根开始做依赖背包,时间复杂度也是O(nmlogn)。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=505;
const int M=4005;
const int inf=1000000000;

int n,m,c[N],d[N],w[N],f[N][M],last[N],cnt,son[N],size[N],q1[M],q2[M],g[N][M];
struct edge{
  int to,next;}e[N*2];

int read()
{
    int x=0,f=1;char ch=getchar();
    while (ch<'0'|
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值