2023 ICPC Nanjing

2023 ICPC Nanjing

ICPC Nanjing

G

可以选 \(k\) 个物品免费的 01 背包问题。考虑我买了 \(x\) 个物品,现在可以使一些物品免费,最优该如何选。肯定是从价格最高的物品开始选,这样可以留下更多的钱。也就是说对于最终的状态,我所花原价买的物品的价格,一定是要比我选则免费的物品的价格要少的。所以我们对所有物品按照价格升序排序后 dp,当 dp 完第 i 个物品时,我们令其为我们选择原价购买的最后一个物品,其他的物品都从后面选价值最大的 \(k\) 个。

void solve() {
    int n = 0, W = 0, k = 0;
    std::cin >> n >> W >> k;
    std::vector p(n, std::pair<int, i64>{});
    for (auto &[w, v] : p) {
        std::cin >> w >> v;
    }
    std::sort(p.begin(), p.end());

    std::vector suf(n + 1, 0ll);
    std::priority_queue<i64, std::vector<i64>, std::greater<i64>> q;
    for (int i = 1; i <= k; ++i) {
        q.push(p[n - i].second);
        suf[n - k] += p[n - i].second;
    }

    for (int i = n - k - 1; i >= 0; --i) {
        suf[i] = suf[i + 1];
        if (not q.empty() && p[i].second > q.top()) {
            suf[i] -= q.top();
            q.pop();
            suf[i] += p[i].second;
            q.push(p[i].second);
        }
    }

    i64 ans = 0;
    std::vector f(W + 1, 0ll);
    for (int i = 0; i < n; ++i) {
        for (int j = W; j >= p[i].first; --j) {
            f[j] = std::max(f[j], f[j - p[i].first] + p[i].second);
        }
        ans = std::max(ans, f[W] + suf[i + 1]);
    }
    std::cout << std::max(ans, suf[0]) << '\n';
    return;
}

A

我们一个一个连通块地考虑,如果一个连通块不是除自己外的任意一个连通块的子图(包括完全相等的情况),我们就说这个连通块中的点都是有可能留到最后的,否则就不是。

实现方面,枚举每个连通块,枚举这个连通块的起点坐标来进行匹配,如果一次枚举过程中,该连通块中的点没出出局的情况(掉在洞里或越界)我们就认为它被某个连通块包含,是一个连通块的子图。

M

首先拆掉 min。有

\[min(f_i, g_i) = f_i + g_i - max_{j = 1}^{n}(a_j) \]

因为对于任意的 \(i\)\(f_i\) \(g_i\) 中必有一个是 \(max_{j = 1}^{n}(a_j)\),取最小值就是两者之和减去这个最大值。

于是我们就可以单独地维护 \(f_i\)\(g_i\)\(a_i\) 的和,对于 \(f_i\) 可以设计以下类来维护插入值的操作和求和的操作

// 用 set 维护单调栈
class set {
public:
    set(int _n) {
        n = _n;
        s.clear();
        // 最开始插入一个最小的数
        // 最后插入一个最大的数
        s.insert({ 0, 0ll });
        s.insert({ n + 1, Inf });
        sum = 0ll;
    }
    void insert(int pos, i64 val) {
        s.insert({ pos, val });
        auto it = s.find({ pos, val });
        auto lst = std::prev(it);
        auto nxt = std::next(it);
        // 上一个元素大于等于插入的元素就直接不插入
        if ((*lst).second >= (*it).second) {
            s.erase(it);
            return;
        }

        sum -= len(*lst, *nxt);
        sum += len(*it, *nxt);
        sum += len(*lst, *it);
        if ((*it).first == (*lst).first) {
            s.erase(lst);
        }
        while ((*nxt).second <= (*it).second) {
            auto tmp = std::next(nxt);
            sum -= len(*it, *nxt);
            sum -= len(*nxt, *tmp);
            sum += len(*it, *tmp);
            s.erase(nxt);
            nxt = tmp;
        }
    }
    i64 get() {
        return sum;
    }
private:
    int n;
    std::set<std::pair<int, i64>> s;
    i64 sum;
    
    i64 len(std::pair<int, i64> l, std::pair<int, i64> r) {
        return 1ll * (r.first - l.first) * l.second;
    }
};
posted @ 2025-10-12 00:18  Young_Cloud  阅读(9)  评论(0)    收藏  举报