1 条题解

  • 0
    @ 2026-9-23 23:22:13

    不妨考虑一个 DP。设 fi,jf _ {i, j} 表示依次考虑 [i,n][i, n] 中的物品,带上了 jj 个,最小代价。由于是正扫,能加就加,因此倒着 DP。

    观察大样例不难发现 fif _ i 是单调的,带的物品越多,最小代价越大。

    考虑转移。

    钦定 fi,0=0f _ {i, 0} = 0,这是有道理的。

    设 kk 满足 fi+1,k<wif _ {i + 1, k} < w _ i,并且最大。

    • 对于 j≤kj \le k,fi,jf _ {i, j} 可以直接继承 fi+1,jf _ {i + 1, j} 的值(也就是不选取 ii),并且这是最优的,因为如果选取 ii 的话代价一定大于 wiw _ i。

    • 对于 j>kj > k,是不可以不选取 ii 直接继承的,因为 fi+1,j≥wif _ {i + 1, j} \ge w _ i,从前往后扫到 ii,能拿就直接拿了,所以 fi,j←fi+1,j−1+wif _ {i, j} \gets f _ {i + 1, j - 1} + w _ i。

    直接做是 O(n2)O(n ^ 2) 的。

    第一部分转移就是保持不变;第二部分,形如先对 [k+1,n−i][k + 1, n - i] 区间加 wiw _ i,然后在第 k+1k + 1 个数前插入 fk+wif _ k + w _ i。

    容易数据结构优化,用平衡树维护区间加和单点插入即可,kk 这个位置可以在平衡树上二分找到(或者 FHQ-treap 这种树,直接按权值分裂)。

    时间复杂度 O(nlog⁡n)O(n \log n)。

    #include <bits/stdc++.h>
    #define F(i, a, b) for(int i = (a); i <= (b); ++i)
    #define dF(i, a, b) for(int i = (a); i >= (b); --i)
    
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ull;
    typedef unsigned int uint;
    typedef pair<int, LL> pii;
    const int N = 5e5 + 5;
    
    int n, w[N];
    
    int rt, ls[N], rs[N], rnd[N]; LL val[N], tag[N];
    static mt19937 Rand;
    uniform_int_distribution<int> rng(1, 1e9);
    void Maketag(int u, LL k) { val[u] += k, tag[u] += k; }
    void Pushdown(int u) {
      if (ls[u]) Maketag(ls[u], tag[u]);
      if (rs[u]) Maketag(rs[u], tag[u]);
      tag[u] = 0;
    }
    int Merge(int u, int v) {
      if (!u || !v) return u | v;
      Pushdown(u), Pushdown(v);
    	if (rnd[u] >= rnd[v]) return rs[u] = Merge(rs[u], v), u;
    	else return ls[v] = Merge(u, ls[v]), v;
    }
    void Split(LL k, int &x, int &y, int u) {
    	if (!u) return x = y = 0, void();
      Pushdown(u);
    	if (val[u] <= k) Split(k, rs[x = u], y, rs[u]);
    	else Split(k, x, ls[y = u], ls[u]);
    }
    int Query(int u) {
      if (!u) return 0;
      while (rs[u]) Pushdown(u), u = rs[u];
      return val[u];
    }
    void Newnode(int u, int k) { val[u] = k, tag[u] = 0, rnd[u] = rng(Rand); }
    void Solve(int u) {
      Pushdown(u);
      if (ls[u]) Solve(ls[u]);
      cout << val[u] << " ";
      if (rs[u]) Solve(rs[u]);
    }
    
    int main() {
      // freopen("zyq.in", "r", stdin);
      // freopen("zyq.out", "w", stdout);
      ios::sync_with_stdio(0);
      cin.tie(0), cout.tie(0);
      cin >> n;
      F(i, 1, n) cin >> w[i];
      Newnode(rt = n, w[n]);
      dF(i, n - 1, 1) {
        int u = 0, v = 0; Split(w[i] - 1, u, v, rt);
        Newnode(i, Query(u) + w[i]), Maketag(v, w[i]);
        rt = Merge(Merge(u, i), v);
      }
      Solve(rt);
      return 0;
    }
    
    • 1

    [POI 2020/2021 R2] 收拾背包 / Pakowanie plecaka

    信息

    ID
    7535
    时间
    16000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者