【BZOJ4182】Shopping(点分治,树上多重背包,单调队列)

Description

马上就是小苗的生日了,为了给小苗准备礼物,小葱兴冲冲地来到了商店街。商店街有n个商店,并且它们之间的道路构成了一颗树的形状。

第i个商店只卖第i种物品,小苗对于这种物品的喜爱度是wi,物品的价格为ci,物品的库存是di。但是商店街有一项奇怪的规定:如果在商店u,v买了东西,并且有一个商店w在u到v的路径上,那么必须要在商店w买东西。小葱身上有m元钱,他想要尽量让小苗开心,所以他希望最大化小苗对买
到物品的喜爱度之和。这种小问题对于小葱来说当然不在话下,但是他的身边没有电脑,于是他打电话给同为OI选手的你,你能帮帮他吗?


Solution

对于一般的树上背包,时间复杂度是 O(nm) O ( n m ) 的。对于多重背包,我们用二进制优化一下,时间复杂度为 O(nmlog) O ( n m log ) 。用单调队列优化,时间复杂度 O(nm) O ( n m ) 。枚举每个点为必选点,时间复杂度 O(n2m) O ( n 2 m )
考虑点分治,每次从分治重心跑树上依赖背包,这样就将时间复杂度优化成了 O(mnlogn) O ( m n log ⁡ n )


Code

/************************************************
 * Au: Hany01
 * Date: Aug 29th, 2018
 * Prob: BZOJ4182 Shopping
 * Email: [email protected] & [email protected]
 * Inst: Yali High School
************************************************/

#include

using namespace std;

typedef long long LL;
typedef long double LD;
typedef pair<int, int> PII;
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define SZ(a) ((int)(a).size())
#define ALL(a) a.begin(), a.end()
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia

template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }

inline int read() {
    static int _, __; static char c_;
    for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

const int maxn = 503, maxm = 4003;

int n, m, beg[maxn], v[maxn << 1], e, nex[maxn << 1], dp[maxn][maxm], q[maxm], head, tail, W[maxn], V[maxn], C[maxn], vis[maxn], Ans, rt, mxch[maxn], sz[maxn], ns;

inline void add(int uu, int vv) { v[++ e] = vv, nex[e] = beg[uu], beg[uu] = e; }

void getrt(int u, int pa) {
    mxch[u] = 0, sz[u] = 1;
    for (register int i = beg[u]; i; i = nex[i]) if (v[i] != pa && !vis[v[i]])
        getrt(v[i], u), sz[u] += sz[v[i]], chkmax(mxch[u], sz[v[i]]);
    chkmax(mxch[u], ns - sz[u]);
    if (mxch[u] < mxch[rt]) rt = u;
}

void DP(int u, int pa, int wsum) {
    for (register int j = beg[u]; j; j = nex[j]) if (v[j] != pa && !vis[v[j]]) {
        //if (W[v[j]]) {
            rep(p, W[v[j]]) {
                q[head = tail = 0] = wsum;
                for (int i = p + wsum; i <= m; i += W[v[j]]) {
                    while (head <= tail && q[head] + C[v[j]] * W[v[j]] < i) ++ head;
                    if (head <= tail) dp[v[j]][i] = dp[u][q[head]] + (i - q[head]) / W[v[j]] * V[v[j]];
                    else dp[v[j]][i] = -INF;
                    for (q[++ tail] = i; head < tail && dp[u][q[tail]] + (i - q[tail]) / W[v[j]] * V[v[j]] > dp[u][q[tail - 1]] + (i - q[tail - 1]) / W[v[j]] * V[v[j]]; -- tail) q[tail - 1] = q[tail];
                }
            }
        //} else For(i, 0, m) dp[v[j]][i] = dp[u][i] + C[v[j]] * V[v[j]];
        DP(v[j], u, wsum + W[v[j]]);
        For(i, wsum + W[v[j]], m) chkmax(dp[u][i], dp[v[j]][i]);
    }
}

void solve(int u) {
    //if (W[u]) {
        For(i, 0, W[u] - 1) dp[u][i] = -INF;
        For(i, W[u], m) dp[u][i] = min(i / W[u], C[u]) * V[u];
    //} else For(i, 0, m) dp[u][i] = C[u] * V[u];
    DP(u, 0, W[u]), chkmax(Ans, dp[u][m]);

    vis[u] = 1;
    for (register int i = beg[u]; i; i = nex[i]) if (!vis[v[i]])
        ns = sz[v[i]], rt = 0, getrt(v[i], 0), solve(v[i]);
}

int main()
{
#ifdef hany01
    freopen("bzoj4182.in", "r", stdin);
    freopen("bzoj4182.out", "w", stdout);
#endif

    static int uu, vv, T;

    for (T = read(), mxch[0] = INF; T --; ) {
        n = read(), m = read(), Ans = e = 0;
        For(i, 1, n) V[i] = read(), beg[i] = vis[i] = 0;
        For(i, 1, n) W[i] = read();
        For(i, 1, n) C[i] = read();
        For(i, 2, n) uu = read(), vv = read(), add(uu, vv), add(vv, uu);
        rt = 0, ns = n, getrt(1, 0), solve(rt), printf("%d\n", Ans);
    }

#ifdef hany01
    cerr << clock() * 1. / CLOCKS_PER_SEC << endl;
#endif

    return 0;
}

你可能感兴趣的:(BZOJ,DP,点分治,树形DP,背包问题)