bzoj4182 Shopping 点分治+单调队列优化多重背包
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=4182
题解
有一个很直观的想法是设 \(dp[x][i]\) 表示在以 \(x\) 为根的子树中选择一个总花费不超过 \(i\) 的以 \(x\) 为根的连通块的最大收益。
可惜,很不幸的是,这样做的时间复杂度无法像一般的树上背包和序列背包一样被保证。能够被保证复杂度的方法只有(可能是我只会)第二维与子树大小有关的方法,或者是将树转化成 dfs 序,然后在序列上做背包。
第二种方法具体的来说就是一次背包转移的时候,如果当前这一位选了物品,就直接从 \(i-1\) 转移,否则必须跳过这一棵子树,也就是从 \(i-siz[i]\) 转移。
但是这样只能求出来的是包含根的连通块的答案。于是我们可以采用点分治来弥补这个问题。
这样,总的时间复杂度为为 \(O(nm\log n)\),其中 \(nm\) 来源于单调队列优化的多重背包,\(\log n\) 来源于点分治。
代码如下:
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 500 + 7;
const int M = 4000 + 7;
const int INF = 0x3f3f3f3f;
int n, m, rt, mima, sum, dfc, ans;
int w[N], c[N], d[N];
int vis[N], siz[N], sq[N], dfn[N], q[M], p[M], dp[N][M];
struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }
inline void getrt(int x, int fa = 0, int dep = 0) {
int f = 0; siz[x] = 1;
for fec(i, x, y) if (!vis[y] && y != fa) getrt(y, x), siz[x] += siz[y], smax(f, siz[y]);
smax(f, sum - siz[x]);
if (smin(mima, f)) rt = x;
}
inline void dfs(int x, int fa = 0) {
siz[x] = 1;
for fec(i, x, y) if (!vis[y] && y != fa) dfs(y, x), siz[x] += siz[y];
dfn[x] = ++dfc, sq[dfc] = x;
}
inline void calc(int x) {
dfc = 0, dfs(x);
for (int i = 1; i <= dfc; ++i) {
int x = sq[i], hd = 1, tl = 0;
for (int j = 0; j < c[x]; ++j) {
hd = 1, tl = 0;
for (int k = 0; k * c[x] + j <= m; ++k) {
int v = k * c[x] + j, y = dp[i - 1][v] - k * w[x];
while (hd <= tl && q[hd] < k - d[x]) ++hd;
if (hd <= tl) dp[i][v] = std::max(p[hd] + k * w[x], dp[i - siz[x]][v]);
else dp[i][v] = dp[i - siz[x]][v];
while (hd <= tl && y >= p[tl]) --tl;
q[++tl] = k, p[tl] = y;
}
}
}
smax(ans, dp[dfc][m]);
}
inline void solve(int x) {
vis[x] = 1, calc(x);
for fec(i, x, y) if (!vis[y]) {
mima = sum = siz[y];
getrt(y), solve(rt);
}
}
inline void work() {
mima = sum = n;
getrt(1), solve(rt);
printf("%d\n", ans);
}
inline void cls() {
memset(vis, 0, sizeof(vis));
memset(head, 0, sizeof(head));
ans = tot = 0;
}
inline void init() {
cls();
read(n), read(m);
for (int i = 1; i <= n; ++i) read(w[i]);
for (int i = 1; i <= n; ++i) read(c[i]);
for (int i = 1; i <= n; ++i) read(d[i]);
int x, y;
for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
int T;
read(T);
while (T--) {
init();
work();
}
fclose(stdin), fclose(stdout);
return 0;
}
bzoj4182 Shopping 点分治+单调队列优化多重背包的更多相关文章
- POJ 1742 (单调队列优化多重背包+混合背包)
(点击此处查看原题) 题意分析 给你n种不同价值的硬币,价值为val[1],val[2]...val[n],每种价值的硬币有num[1],num[2]...num[n]个,问使用这n种硬币可以凑齐[1 ...
- [Bzoj4182]Shopping(点分治)(树上背包)(单调队列优化多重背包)
4182: Shopping Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 374 Solved: 130[Submit][Status][Disc ...
- HDU 2191 - 单调队列优化多重背包
题目: 传送门呀传送门~ Problem Description 急!灾区的食物依然短缺! 为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种 ...
- POJ 1276 Cash Machine(单调队列优化多重背包)
Cash Machine Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 38986 Accepted: 14186 De ...
- [小明打联盟][斜率/单调队列 优化dp][背包]
链接:https://ac.nowcoder.com/acm/problem/14553来源:牛客网 题目描述 小明很喜欢打游戏,现在已知一个新英雄即将推出,他同样拥有四个技能,其中三个小技能的释放时 ...
- 单调队列优化DP,多重背包
单调队列优化DP:http://www.cnblogs.com/ka200812/archive/2012/07/11/2585950.html 单调队列优化多重背包:http://blog.csdn ...
- bzoj1531[POI2005]Bank notes 单调队列优化dp
1531: [POI2005]Bank notes Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 559 Solved: 310[Submit][Sta ...
- 算法笔记--单调队列优化dp
单调队列:队列中元素单调递增或递减,可以用双端队列实现(deque),队列的前面和后面都可以入队出队. 单调队列优化dp: 问题引入: dp[i] = min( a[j] ) ,i-m < j ...
- [DP浅析]线性DP初步 - 2 - 单调队列优化
目录 #0.0 前置知识 #1.0 简单介绍 #1.1 本质 & 适用范围 #1.2 适用方程 & 条件 #2.0 例题讲解 #2.1 P3572 [POI2014]PTA-Littl ...
随机推荐
- php怎么启动exe文件
PHP作为一种服务器端的脚本语言,象编写简单,或者是复杂的动态网页这样的任务,它完全能够胜任.但事情不总是如此,有时为了实现某个功能,必须借助于操作系统的外部程序(或者称之为命令),这样可以做到事半功 ...
- 科讯使用的:ckeditor编辑器.复制word图片.一直沾不上去.谁有好的解决办法呢
在之前在工作中遇到在富文本编辑器中粘贴图片不能展示的问题,于是各种网上扒拉,终于找到解决方案,在这里感谢一下知乎中众大神以及TheViper. 通过知乎提供的思路找到粘贴的原理,通过TheViper找 ...
- 用三目运算,与if判断 函数调用 达到相同判定作用
三目运算符: 操作数1 ? 操作数2 : 操作数3 (操作数1位bool类型,操作数2和操作数3为两个相同的任何类型) 返回结果:如果操作数1判定结果为真,则将操作数2作为返回结果如果操作 ...
- vue组件传值之父传子
1.父组件给子组件传值 home父组件 header子组件 关键字props home代码 <template> <div> <v-header :title=&q ...
- OpenCV笔记:pyrDown()函数和pryUp()函数的使用
OpenCV实现了用于创建图像金字塔的两个函数pyrDown()和pryUp(). 图像金字塔是一种经典的图像多尺寸描述方法,它将降采样和平滑滤波结合在一起,对图像进行多尺度表示.图像金字塔由不同尺寸 ...
- What is httpcontext
https://docs.microsoft.com/en-us/dotnet/api/system.web.httpcontext?view=netframework-4.8 Encapsulate ...
- nginx用途
Nginx常用来做静态内容服务器和代理服务器,用来放置静态资源或者转发请求给后面的应用服务. 1. Nginx作为静态服务器使用 作为一个Web服务器,其最主要的任务是作为静态服务器使用. 你需要将 ...
- leetcode 189. 旋转数组(python)
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数. 示例 1: 输入: [1,2,3,4,5,6,7] 和 k = 3输出: [5,6,7,1,2,3,4]解释:向右旋转 1 步: ...
- 【转载】阿里入局,独角兽估值30亿美金,谈谈RPA是什么
缩短法定工作时间,已成国际劳动立法趋势,全球政府都曾面对这样的议题,过往企业IT也在思考这件事,开发出更好的软件系统帮助员工,就是普遍作法,这也已经行之有年,而现阶段最有效的作法,则是要用AI来提供帮 ...
- Python笔记(十九)_继承
继承 继承可以把父类的所有功能都直接拿过来,这样就不必从零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写 多重继承 通过多重继承,一个子类就可以同时获得多个父类的所有功能 > ...