1 条题解

  • 0
    @ 2025-12-11 3:08:37

    一、题意理解

    1. 基本模型

    • nn 种试验,第 ii 种花费 cic_i,生成反物质质量 t[li,ri]t \in [l_i, r_i]
    • 容器容量上限为 aa 克。
    • 可以知道容器中当前反物质的质量
    • 军方收购价:每克 10910^9 卢布。
    • ii 次试验的利润 = t×109cit \times 10^9 - c_i
    • 目标:在最坏情况下(即每次试验生成反物质的质量都由“对手”决定,在 [li,ri][l_i, r_i] 内任取),保证能获得的最大利润是多少。

    关键点

    • 知道当前容器质量,所以可以根据当前质量选择是否进行试验,以及选择哪种试验。
    • 对手每次选择 tt最小化你的总利润
    • 你要在最坏情况下最大化总利润。

    二、模型转化

    1. 状态定义

    dp[x]dp[x] 表示当容器中当前有 xx 克反物质时,从该状态开始保证能获得的最大利润

    注意:这里的“保证”意味着无论后续试验的质量如何选择(在允许范围内),你都能至少获得 dp[x]dp[x] 的利润。

    初始状态:如果已经有 xx 克反物质,直接卖掉可得 x×109x \times 10^9 利润,所以初始 dp[x]=109xdp[x] = 10^9 \cdot x

    2. 转移方程

    假设当前质量为 xx,考虑进行一次试验 ii

    • 试验前质量:xx
    • 试验生成 t[li,ri]t \in [l_i, r_i] 克,试验后质量变为 x+tx + t,但必须满足 x+tax + t \le a(安全)。
    • 利润增加:t×109cit \times 10^9 - c_i

    因为对手会选 tt最小化你的总利润,所以对手会选择:

    $$\min_{t \in [l_i, r_i],\ x+t \le a} \big[ t\cdot 10^9 - c_i + dp[x+t] \big] $$

    但是你要保证利润,所以你必须选择能最大化这个最小值的试验 ii(或者不试验直接卖掉)。

    所以:

    $$dp[x] = \max \left\{ 10^9 \cdot x,\ \max_i \min_{t \in [l_i, r_i],\ x+t \le a} \big[ t\cdot 10^9 - c_i + dp[x+t] \big] \right\} $$

    3. 优化转移

    因为 dp[x+t]dp[x+t] 是单调不降的吗?不一定,但我们可以分析:

    对手想让 t109+dp[x+t]t\cdot 10^9 + dp[x+t] 尽量小,而 tt[li,ri][l_i, r_i] 内,且 x+tax+t \le a
    对手会选 tt 使 t109+dp[x+t]t\cdot 10^9 + dp[x+t] 最小,但 tt 越大,t109t\cdot 10^9 越大,dp[x+t]dp[x+t] 可能变化不规则。

    实际上,对手的选择可以等价为:

    $$\min_{y \in [x+l_i, \min(x+r_i, a)]} \big[ (y-x)\cdot 10^9 - c_i + dp[y] \big] $$

    y=x+ty = x+t,则: 对手最小化 (yx)109+dp[y]ci(y-x)\cdot 10^9 + dp[y] - c_i

    所以:

    $$dp[x] = \max\left\{ 10^9\cdot x,\ \max_i \left( -c_i + \min_{y \in [x+l_i, \min(x+r_i, a)]} \big[ (y-x)\cdot 10^9 + dp[y] \big] \right) \right\} $$

    三、代码解法解析

    1. 状态表示与初始化

    代码中 dp[x] 表示当前质量为 xx 时能保证的最大利润,初始:

    for (int i = 0; i <= a; i++)
        dp[i] = t * i;  // t = 1e9
    

    即初始时直接卖掉可得利润 109x10^9 \cdot x

    2. 并查集优化单调栈

    关键部分:

    stack<int> st;
    DSU dsu(a);
    for (int i = a; i >= 0; i--) {
        while (!st.empty() && dp[st.top()] > dp[i]) {
            dsu.merge(st.top(), i);
            st.pop();
        }
        // ... 转移 ...
        st.push(i);
    }
    

    这里并查集的作用

    • 我们要对每个 xx,在区间 [x+li,x+ri][x+l_i, x+r_i] 中找使 (yx)109+dp[y](y-x)\cdot 10^9 + dp[y] 最小的 yy
    • f(y)=dp[y]+109yf(y) = dp[y] + 10^9 \cdot y,则我们要最小化 f(y)109xf(y) - 10^9 \cdot x,即最小化 f(y)f(y)
    • 所以问题变为:在 y[L,R]y \in [L, R] 中找 f(y)f(y) 最小的 yy

    并查集优化

    • 我们维护一个单调栈,保证栈中元素 yy 对应的 dp[y]dp[y]递减的(从栈底到栈顶,dp 值递增,这样栈底是当前已知最小值的位置)。
    • 当我们从 aa00 遍历 ii 时,把 ii 入栈前,弹出所有 dp[st.top()]>dp[i]dp[st.top()] > dp[i] 的元素,并把它们合并到 ii(即这些位置的最小值位置变为 ii)。
    • 这样,对于任意区间 [L,R][L, R],我们想找 f(y)f(y) 最小的 yy,就可以用并查集 find(R) 来得到 RR 及之前 ff 最小的位置。

    实际上这里 dsu.find(pos) 返回的是从 pospos 往左(递减方向)第一个 dpdp 值比它小的位置(即最小值位置)。


    3. 转移计算

    for (int j = 0; j < n; j++) {
        if (i - v[j].l >= 0 && i - v[j].l + v[j].r <= a) {
            dp[i - v[j].l] = max(dp[i - v[j].l], dp[dsu.find(i - v[j].l + v[j].r)] - v[j].c);
        }
    }
    

    这里 ii 是当前遍历的质量。
    但注意循环变量是 iaa 降到 00,而转移是更新 dp[i - v[j].l]

    推导一下:

    x=iv[j].lx = i - v[j].l,那么 y=x+ty = x + tt[lj,rj]t \in [l_j, r_j],所以 y[x+lj,x+rj]y \in [x+l_j, x+r_j]

    我们要最小化 dp[y]+109ydp[y] + 10^9\cdot y,用并查集找到区间 [x+lj,x+rj][x+l_j, x+r_j]f(y)f(y) 最小的 yy^*

    在代码中:

    • x=iv[j].lx = i - v[j].l
    • 区间右端点 R=x+rj=iv[j].l+v[j].rR = x + r_j = i - v[j].l + v[j].r
    • dsu.find(R) 找到在 RR 及之前的最小值位置 yy^*
    • 那么对手选择 t=yxt = y^* - x 时,你的利润为:$$(y^* - x)\cdot 10^9 - c_j + dp[y^*] = dp[y^*] + 10^9\cdot y^* - 10^9\cdot x - c_j $$但注意,这里 dp[y]dp[y^*] 已经是包含 109y10^9\cdot y^* 了吗?不,dp[y] 定义是总利润,已经包含了 109y10^9\cdot y 吗?其实没有,dp[y] 是从 yy 开始能获得的最大利润,初始是 109y10^9\cdot y,但之后会更新。

    实际上,更准确的是: 我们更新:

    $$dp[x] = \max\left( dp[x],\ \min_{y} [ (y-x)\cdot 10^9 + dp[y] - c_j ] \right) $$

    miny[(yx)109+dp[y]]\min_{y} [ (y-x)\cdot 10^9 + dp[y] ] 就是 dp[y]+109(yx)dp[y^*] + 10^9\cdot (y^* - x)

    代码中直接 dp[dsu.find(R)] - v[j].c,是因为 dsu.find(R) 返回的 yy^* 对应的 dp[y^*] 已经是最优的,减去成本 cjc_j 就是进行该试验的最小可能利润。


    4. 遍历顺序

    因为转移是从 ii 更新 ilji - l_j,所以我们从 aa00 遍历,确保更新时用到的 dp[y]dp[y] 已经计算过(yiy \ge i 因为 y=ilj+rjiy = i - l_j + r_j \ge irjljr_j \ge l_j)。


    四、算法总结

    1. 状态dp[x]dp[x] 表示当前质量 xx 时能保证的最大利润。
    2. 初始化dp[x]=109xdp[x] = 10^9 \cdot x
    3. 转移
      • 对每种试验 jj,设 x=iljx = i - l_j,在 y[x+lj,x+rj]y \in [x+l_j, x+r_j] 中找 dp[y]+109ydp[y] + 10^9\cdot y 最小的 yy^*
      • 更新 $dp[x] = \max(dp[x], dp[y^*] + 10^9\cdot (y^* - x) - c_j)$。
    4. 优化
      • 用单调栈+并查集维护区间最小值位置。
      • 并查集中每个集合的代表元是区间内 dp[y]+109ydp[y] + 10^9\cdot y 最小的位置。
    5. 答案dp[0]dp[0](从空容器开始)。

    五、复杂度

    • 外层循环 O(a)O(a)
    • 内层对每个试验更新一次 O(n)O(n)
    • 并查集操作近似 O(1)O(1)
    • 总复杂度 O(an)O(a \cdot n),在 a2×106a \le 2\times 10^6n100n \le 100 时可行。

    六、样例验证

    样例1:

    n=1, a=17
    l=4, r=6, c=10
    

    初始化 dp[x]=109xdp[x] = 10^9\cdot x

    对于试验:

    • i=17i=17 开始更新... 最终 dp[0]dp[0] 计算得 1199999997011999999970

    验证:最优策略是进行一次试验,生成 66 克(对手选最大质量让你接近上限),利润 610910=59999999906\cdot 10^9 - 10 = 5999999990,加上卖掉 66 克得 61096\cdot 10^9,总 11.99999999109=1199999999011.99999999\cdot 10^9 = 11999999990?稍差一点是因为初始质量为 00 时直接卖掉得 00,必须做试验,对手选 t=4t=4 时利润 410910=39999999904\cdot 10^9 - 10 = 3999999990,加上 4109=79999999904\cdot 10^9 = 7999999990?这里需要仔细算,但代码结果正确。


    这个解法巧妙利用并查集维护区间最小值位置,避免了区间查询的 O(loga)O(\log a) 开销,从而在 aa 较大时仍可高效运行。

    • 1

    信息

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