专栏文章

题解:CF601E A Museum Robbery

CF601E题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miprii5w
此快照首次捕获于
2025/12/03 16:45
3 个月前
此快照最后确认于
2025/12/03 16:45
3 个月前
查看原文

动态时间区间背包问题题解

写在前面

一道很好的模板题,主要是想到构建时间轴,前面有大概思路,不过细节都在代码里了。

问题描述

给定一个初始物品列表和包含三种操作的指令序列:
  1. 添加物品(重量w, 价值v
  2. 删除指定物品
  3. 查询当前背包最优解(哈希值输出)
要求处理这些操作并正确响应每个查询。

算法思路

采用时间轴线段树结合分层背包DP的解决方案:我们离线记录时间,然后把该时间存在的物品挂在区间内。那么不难想到,叶子结点就是统计答案的时候。

核心思想

  1. 时间离散化:将每个操作转化为时间区间上的事件
  2. 线段树分治:物品按生效时间区间存入线段树节点
  3. DP持久化:递归处理线段树时传递DP状态
可以结合代码参考一下细节部分

完整代码

CPP
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 1e5 + 10;
const ll base = 1e7 + 19;
const ll mod = 1e9 + 7;

struct Item { 
    int st, ed; // 生效起止时间
    int vol, val; // 体积和价值
};
vector<Item> items; // 所有物品记录
int n, q, max_vol, time_cnt = 1; // 当前时间点初始化

struct SegNode {
    int l, r;
    vector<Item> items; // 本节点时间区间内的物品
} tree[N << 2];

// 建树
void build(int p, int l, int r) {
    tree[p] = {l, r, {}};
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(p << 1, l, mid);
    build(p << 1 | 1, mid + 1, r);
}

// 将物品插入对应时间区间
void update(int p, int l, int r, const Item& item) {
    if (tree[p].l >= l && tree[p].r <= r) {
        tree[p].items.push_back(item);
        return;
    }
    int mid = (tree[p].l + tree[p].r) >> 1;
    if (l <= mid) update(p << 1, l, r, item);
    if (r > mid) update(p << 1 | 1, l, r, item);
}

// 分治处理背包DP
void solve(int p, vector<ll>& dp) {
    auto new_dp = dp; // 复制当前DP状态
    
    // 用本节点物品更新背包
    for (auto& item : tree[p].items)
        for (int j = max_vol; j >= item.vol; j--)
            new_dp[j] = max(new_dp[j], new_dp[j - item.vol] + item.val);

    // 到达查询时间点
    if (tree[p].l == tree[p].r) {
        ll hash = 0;
        for (int j = max_vol; j >= 1; j--)
            hash = (hash * base + new_dp[j]) % mod;
        cout << hash << "\n";
    } else {
        solve(p << 1, new_dp); // 处理左子树
        solve(p << 1 | 1, new_dp); // 处理右子树
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    
    // 初始物品输入
    cin >> n >> max_vol;
    for (int i = 0; i < n; i++) {
        int v, w; cin >> w >> v;
        items.push_back({1, -1, v, w}); // -1表示永久生效
    }
    
    // 处理操作序列
    cin >> q;
    while (q--) {
        int op; cin >> op;
        if (op == 1) { // 添加物品
            int v, w; cin >> w >> v;
            items.push_back({time_cnt, -1, v, w});
        } else if (op == 2) { // 删除物品
            int x; cin >> x;
            items[x - 1].ed = time_cnt - 1; // 设置结束时间
        } else { // 时间推进
            time_cnt++;
        }
    }

    // 构建时间线段树
    build(1, 1, time_cnt);
    
    // 将物品插入对应时间区间
    for (auto& item : items) {
        if (item.ed == -1) item.ed = time_cnt;
        if (item.st <= item.ed) 
            update(1, item.st, item.ed, item);
    }

    // 初始化DP数组并求解
    vector<ll> dp(max_vol + 1, 0);
    solve(1, dp);
    
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...