bzoj4182: Shopping
Description
马上就是小苗的生日了,为了给小苗准备礼物,小葱兴冲冲地来到了商店街。商店街有n个商店,并且它们之间的道路构成了一颗树的形状。
第i个商店只卖第i种物品,小苗对于这种物品的喜爱度是wi,物品的价格为ci,物品的库存是di。但是商店街有一项奇怪的规定:如果在商店u,v买了东西,并且有一个商店w在u到v的路径上,那么必须要在商店w买东西。小葱身上有m元钱,他想要尽量让小苗开心,所以他希望最大化小苗对买
到物品的喜爱度之和。这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为OI选手的你,你能帮帮他吗?
Input
输入第一行一个正整数T,表示测试数据组数。
对于每组数据,
第一行两个正整数n;m;
第二行n个非负整数w1,w2…wn;
第三行n个正整数c1,c2…cn;
第四行n个正整数d1,d2…dn;
接下来n-1行每行两个正整数u;v表示u和v之间有一条道路
Output
输出共T 行,每行一个整数,表示最大的喜爱度之和。
Sample Input
1
3 2
1 2 3
1 1 1
1 2 1
1 2
1 3
Sample Output
4
HINT
N<=500,M<=4000,T<=5,Wi<=4000,Di<=100
知识点:单调队列优化多重背包
首先写出多重背包的式子
然后关键的一步就是考虑使用在
意义下的转移方程
于是有令
方程改为
令
我们先枚举
,再枚举
,再枚举
枚举
的过程可以用单调队列优化。
于是复杂度就可以降到
多重背包优化代码
void Pack(int cur, int c, int v, int l) {
for(int j = 0, La, Ra, Lb, Rb; j < c; ++j) {
La = Ra = Lb = Rb = 0;
for(int k = j, i = 0; k <= m; k += c, ++i) {
if(Rb == Lb + l + 1)
if(qa[La + 1] == qb[++Lb]) ++La;
int t = f[cur + 1][k] - i * v;
while(La < Ra && qa[Ra] <= t) --Ra;
qa[++Ra] = qb[++Rb] = t;
f[cur][k] = qa[La + 1] + i * v;
}
}
}
分析
如果不考虑树,就是一道多重背包。
考虑树,可以枚举根,对于树块上的01背包,我们有一个经典的方程
也就是分成选或不选两个部分
对于多重背包,我们只需要特殊考虑选的过程。
转移基本相同,就是多了一个必须选的条件。
考虑多重背包里不选的情况。也就是
的情况。
跳过这步转移即可。
然而这样的复杂度仍然是
的
上点分治即可。
总复杂度
代码
/**************************************************************
Problem: 4182
User: 2014lvzelong
Language: C++
Result: Accepted
Time:10428 ms
Memory:9244 kb
****************************************************************/
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int M = 4005, N = 505, inf = 0x3f3f3f3f;
int read() {
char ch = getchar(); int x = 0, f = 1;
for(;ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
int siz[N], dfn[N], pre[N], c[N], l[N], v[N], qa[M], qb[M];
int nxt[N << 1], to[N << 1], f[N][M], ans, top, cnt, mx, G, m, n;
bool vis[N];
void add(int u, int v) {to[++top] = v; nxt[top] = pre[u]; pre[u] = top;}
void adds(int u, int v) {add(u, v); add(v, u);}
void Root(int u, int fa) {
siz[u] = 1; int tp = 0;
for(int i = pre[u]; i; i = nxt[i])
if(to[i] != fa && !vis[to[i]]) {
Root(to[i], u); siz[u] += siz[to[i]];
tp = max(tp, siz[to[i]]);
}
tp = max(tp, cnt - siz[u]);
if(tp < mx) mx = tp, G = u;
}
void Pack(int cur, int c, int v, int l) {
for(int j = 0, La, Ra, Lb, Rb; j < c; ++j) {
La = Ra = Lb = Rb = 0;
for(int k = j, i = 0; k <= m; k += c, ++i) {
if(Rb == Lb + l + 1)
if(qa[La + 1] == qb[++Lb]) ++La;
if(La < Ra) f[cur][k] = qa[La + 1] + i * v;
int t = f[cur + 1][k] - i * v;
while(La < Ra && qa[Ra] <= t) --Ra;
qa[++Ra] = qb[++Rb] = t;
}
}
}
void Dp() {
for(int i = top, k; i; --i) {
k = dfn[i]; Pack(i, c[k], v[k], l[k]);
for(int j = 0;j <= m; ++j) f[i][j] = max(f[i][j], f[i + siz[k]][j]);
}
for(int i = 0;i <= m; ++i) ans = max(ans, f[1][i]);
for(int i = 1;i <= top; ++i) for(int j = 0;j <= m; ++j) f[i][j] = 0;
}
void Dfs(int u, int fa) {
siz[u] = 1; dfn[++top] = u;
for(int i = pre[u]; i; i = nxt[i])
if(to[i] != fa && !vis[to[i]]) Dfs(to[i], u), siz[u] += siz[to[i]];
}
void Div(int u) {
top = 0; Dfs(u, 0); Dp(); vis[u] = true;
for(int i = pre[u]; i; i = nxt[i])
if(!vis[to[i]]) {
cnt = siz[to[i]]; mx = inf; G = 0;
Root(to[i], 0); Div(G);
}
}
int main() {
for(int T = read(); T--;) {
n = read(); m = read(); top = 0; ans = 0;
for(int i = 1;i <= n; ++i) vis[i] = pre[i] = 0;
for(int i = 1;i <= n; ++i) v[i] = read();
for(int i = 1;i <= n; ++i) c[i] = read();
for(int i = 1;i <= n; ++i) l[i] = read();
for(int i = 1;i < n; ++i) adds(read(), read());
G = 0; cnt = n; mx = inf; Root(1, 0);
Div(G); printf("%d\n", ans);
}
return 0;
}