1 条题解

  • 0
    @ 2026-5-5 13:33:44

    CF1989D Smithing Skill 题解

    题意简述

    nn 种武器和 mm 种材料。锻造第 ii 种武器消耗 aia_i同种材料,熔毁该武器返还 bib_i 个材料(1bi<ai1061 \le b_i < a_i \le 10^6)。每次锻造得 11 经验,每次熔毁也得 11 经验。初始第 jj 种材料有 cjc_j 个(cj109c_j \le 10^9)。

    你可以无限次锻造与熔毁,任意武器可用任意材料。求能获得的最大总经验值。

    1n,m1061 \le n, m \le 10^6


    转化为循环模型

    锻造+熔毁视为一个循环:消耗 aia_i,返还 bib_i,净消耗 di=aibid_i = a_i - b_i,获得 22 经验。

    锻造的门槛是当前材料数必须 ai\ge a_i。一次循环后材料数变为 cdic - d_i

    如果一直用同一种武器,从 cc 开始能完成的循环次数为:

    $$\left\lfloor \frac{c - a_i}{d_i} \right\rfloor + 1 \quad (c \ge a_i) $$

    等价于 cbidi\left\lfloor \dfrac{c - b_i}{d_i} \right\rfloor


    有效武器的筛选

    对于同一个净消耗 dd,门槛 aa 越小越好。令

    s[d]=min{aiaibi=d}s[d] = \min\{a_i \mid a_i - b_i = d\}

    (若不存在则为 ++\infty)。

    d1<d2d_1 < d_2s[d1]s[d2]s[d_1] \le s[d_2],则武器 22 被武器 11 完全支配。去除被支配的选项后,剩下的武器满足:dd 递增时,s[d]s[d] 递减

    构造两个数组:

    • q[k]q[k]:第 kk 个有效武器的门槛 aa(严格递减);
    • p[k]p[k]:第 kk 个有效武器的净消耗 dd(严格递增)。

    DP 计算 f[i]f[i]

    f[i]f[i] 表示手上有 ii 个同种材料时,能完成的最大循环次数

    对于给定的 ii,在所有满足 aia \le i 的武器中应选哪个?材料多时用 dd 小的武器(循环次数 i/d\approx i/d),材料少时只能用门槛低的武器(dd 大)。由于 "dd 越大 aa 越小",满足 aia \le i 的武器恰好是序列的一段后缀。用指针 nownow 找到后缀起点,然后转移:

    c = i;
    f[i] += (c - q[now]) / p[now];          // 整批循环
    c = (c - q[now]) % p[now] + q[now];    // 剩余材料
    while (c >= q[now]) c -= p[now], f[i]++; // 可能还有最后一次
    f[i] += f[c];                           // 递归到更小的状态
    

    大状态拆成整除部分 + 余数部分,余数递归到已算好的 ff


    回答询问

    对于每种初始材料 cjc_j

    • cj<q[1]c_j < q[1](最优武器门槛都不够):直接 f[cj]f[c_j]
    • 否则,先用最优武器批量消耗到 <q[1]< q[1],再加 f[余数]f[\text{余数}]
    if (c < q[1]) { ans += f[c]; continue; }
    ans += (c - q[1]) / p[1];
    c = (c - q[1]) % p[1] + q[1];
    while (c >= q[1]) c -= p[1], ans++;
    ans += f[c];
    

    最终答案 = 总循环次数 ×2\times 2


    复杂度

    • 预处理 ssO(n+maxa)O(n + \max a)
    • 构建 q,pq,pO(maxa)O(\max a)maxa106\max a \le 10^6
    • DP ffO(maxa)O(\max a)
    • 回答询问:O(m)O(m)

    总复杂度 O(n+m+maxa)O(n + m + \max a),足以通过。


    参考代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N = 1500000;
    int n, m;
    ll a[N], b[N], s[N], q[N], p[N], f[N], cnt = 0, now = 0, ans = 0;
    
    inline ll read() {
        ll x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
        return x * f;
    }
    
    int main() {
        for (int i = 0; i <= 1000000; i++) s[i] = 1e9;
        n = read(); m = read();
        for (int i = 1; i <= n; i++) a[i] = read();
        for (int i = 1; i <= n; i++) b[i] = read();
        for (int i = 1; i <= n; i++) {
            ll d = a[i] - b[i];
            if (a[i] < s[d]) s[d] = a[i];
        }
        cnt = 0;
        q[0] = 1e9; p[0] = 0;
        for (int i = 1; i <= 1000000; i++) {
            if (s[i] < q[cnt]) {
                q[++cnt] = s[i];
                p[cnt] = i;
            }
        }
        now = cnt;
        for (int i = 1; i <= 1000000; i++) {
            while (q[now] <= i && now > 0) now--;
            if (now < cnt) now++;
            else continue;
            ll c = i;
            f[i] += (c - q[now]) / p[now];
            c = (c - q[now]) % p[now] + q[now];
            while (c >= q[now]) c -= p[now], f[i]++;
            f[i] += f[c];
        }
        for (int i = 1; i <= m; i++) {
            ll c = read();
            if (c < q[1]) {
                ans += f[c];
                continue;
            }
            ans += (c - q[1]) / p[1];
            c = (c - q[1]) % p[1] + q[1];
            while (c >= q[1]) c -= p[1], ans++;
            ans += f[c];
        }
        printf("%lld\n", ans * 2);
        return 0;
    }
    
    • 1

    信息

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