Luogu P5391 Solution

闲话

这道题目之所以被称为“青染之心”,不仅是因为这道题与东方有关的故事背景,更是因为这道题将我们的思绪拨回了刚学习树链剖分的时候。

题解

这道题的做法并不显然,毕竟这道题可以让人一眼想到可撤销背包,但是想到操作树远没有这么简单。即使知道了可以使用操作树处理,也很难想到使用树链剖分进行优化。更进一步地,即使知道了树链剖分,也不一定能跳出先处理重链的惯性思维,而改用先处理轻链。

此处具体解释如何通过重链剖分优化本题的空间,关于背包的部分不再赘述。

首先,让我们回想重链剖分拥有的一条重要性质——在一棵有 \(n\) 个节点的树上,从根到任意节点的唯一路径上的轻边数不超过 \(\log n\)

我第一次看到此题时,首先想到的思路是使用重链剖分,从根节点所在的重链开始,一条一条链地处理,中间碰到轻边就将整个 dp 数组拷贝一份等到后面处理。

然而,重链剖分并不保证一棵树最多被剖分成 \(\log n\) 条链。一个节点数 \(n\) 不少于 \(2\) 的菊花图会被剖分成 \((n-1)\) 条链。

于是,在这道题中,我们需要一反常态地先处理轻边。

具体来说,对于操作树上的一个节点 \(x\),我们将挂载在节点 \(x\) 上的物品加入背包后,我们可以先将 dp 数组复制到轻边所连接的节点,并立即递归处理。原因是,在处理完任意一个轻链后,我们可以直接回收这条轻链所占的 dp 数组空间,并将其赋予下一个轻链。

令物品总数为 \(n\),则时间复杂度 \(O(nV)\),空间复杂度 \(O(V\log n)\)

代码

#include <cctype>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
const int N = 2e4 + 10;
int n, q, v, dp[15][N], siz[N], ds[N], idx, res[N], tag[N], f[N], tx[N], ty[N];
basic_string<int> road[N];
template <typename _Tp> inline void read(_Tp &x)
{
    char ch;
    while (ch = getchar(), !isdigit(ch) and ~ch)
        ;
    x = (ch ^ 48);
    while (ch = getchar(), isdigit(ch))
        x = (x << 3) + (x << 1) + (ch ^ 48);
}
#define max(x, y) ((x) > (y) ? (x) : (y))
template <typename _Tp, typename... _Args> inline void read(_Tp &x, _Args &...args)
{
    read(x);
    read(args...);
}
void init(int x)
{
    siz[x] = 1;
    ds[x] = n + 1;
    for (auto &i : road[x])
    {
        init(i);
        if (siz[i] > siz[ds[x]])
            ds[x] = i;
        siz[x] += siz[i];
    }
}
void dfs(int x)
{
    for (int i = tx[x]; i <= v; i++)
    {
        dp[idx][i] = max(dp[idx][i], dp[idx][i - tx[x]] + ty[x]);
        res[x] = max(res[x], dp[idx][i]);
    }
    res[x] = max(res[x], res[f[x]]);
    if (!ds[x])
        return;
    for (auto &i : road[x])
    {
        if (i == ds[x])
            continue;
        idx++;
        memcpy(dp[idx] + 1, dp[idx - 1] + 1, v << 2);
        dfs(i);
        idx--;
    }
    dfs(ds[x]);
}
string s;
int main()
{
    read(q, v);
    for (int i = 1, ta, tb; i <= q; i++)
    {
        tag[i] = tag[i - 1];
        cin >> s;
        if (s == "erase")
        {
            tag[i] = f[tag[i]];
            continue;
        }
        read(ta, tb);
        n++;
        tx[n] = ta, ty[n] = tb;
        road[tag[i]] += n;
        f[n] = tag[i];
        tag[i] = n;
    }
    init(0);
    dfs(0);
    for (int i = 1; i <= q; i++)
    {
        printf("%d\n", res[tag[i]]);
    }
}
posted @ 2024-10-23 20:41  丝羽绫华  阅读(1)  评论(0编辑  收藏  举报