1 条题解

  • 0
    @ 2026-5-5 17:57:45

    题意简述

    给定 nn 个芯片,放置在编号 11xx 的格点上(每个格点可放任意多个芯片)。允许四种操作(可逆),经过任意次操作后,剩余的最少芯片数称为该放置方案的成本
    求恰好 nn 个芯片、成本恰好为 mm 的放置方案数,对 998244353998244353 取模。
    数据范围:1mn1000, 2x101 \le m \le n \le 1000,\ 2 \le x \le 10

    核心观察

    1. 操作可逆性
      所有操作在逆操作下封闭,因此我们可以将最终状态反推到初始状态
      最终状态的最简形式是:若干个位于 11 号点的芯片(因为操作 3、4 可以移动 1、2 号点芯片,且操作 1、2 可以合并芯片到更大位置)。

    2. 斐波那契关系
      通过逆向模拟发现:一个位于位置 ii 的芯片,等价于花费 fif_i 个位于位置 11 的芯片,其中 fif_i 是斐波那契数列(f1=1, f2=1, f3=2,f_1=1,\ f_2=1,\ f_3=2,\ldots)。
      原因:操作 1 的逆操作是“将 i1i-1i2i-2 的两个芯片合并成一个位于 ii 的芯片”,这正是斐波那契的递推。
      因此:

      $$\text{成本} = \text{最少剩余芯片数} = \min\left\{\sum \text{芯片数} \ \big|\ \sum (\text{芯片数} \times f_{\text{位置}}) = \text{总价值} \right\} $$

      其中“总价值”是初始所有芯片按位置 ii 加权 fif_i 后得到的 11 号芯片等价个数。

    3. 价值上界
      f10=55f_{10}=55n1000n \le 1000,因此最大总价值 55×1000=55000\le 55 \times 1000 = 55000
      且由于 fif_i 增长很快,ii 超过 2424fi>55000f_i > 55000,因此有效位置只到 2424,但题目中 x10x \le 10,所以实际只需考虑 1..x1..x

    预处理:最小芯片数对应关系

    定义 c[v]c[v] 表示:用若干 fif_iii 可重复)拼出总价值 vv,所需的最少芯片数(即最少个数)。

    • 初始 c[0]=0c[0]=0,其余 c[v]=c[v]=\infty
    • 完全背包转移:对每个 i[1,x]i \in [1,x],对 vvfif_i5500055000c[v]=min(c[v], c[vfi]+1)c[v] = \min(c[v],\ c[v-f_i] + 1) 注意:这里的 ii 是位置编号,fif_i 是价值,一个芯片占据一个位置,所以每加一个 fif_i 就对应多一个芯片。
      因此 c[v]c[v] 的含义是:生成总价值 vv,最少需要多少个芯片。
      而题目中的“成本”正是这个最少芯片数。
      所以:成本 = m 等价于 c[v]=mc[v] = m

    动态规划:统计方案数

    我们需要统计有多少种放置方案(即每个位置 1..x1..x 上的芯片数),使得:

    • 总芯片数 = nn
    • 总价值 v=i=1xcntifiv = \sum_{i=1}^x cnt_i \cdot f_i 满足 c[v]=mc[v] = m

    dpj,kdp_{j,k} 表示:考虑了前 ii 个位置(滚动数组优化掉第一维),已经放置了 jj 个芯片,总价值为 kk 的方案数。
    初始:dp0,0=1dp_{0,0}=1
    转移:对每个位置 ii1ix1 \le i \le x),枚举这个位置放 tt 个芯片(t0t \ge 0),则:

    dpj+t, k+tfi+=dpj,kdp'_{j+t,\ k + t\cdot f_i} \mathrel{+}= dp_{j,k}

    注意 tt 可到 nn,但 kk 上限为 5500055000,直接转移是 O(xn255000)O(x \cdot n^2 \cdot 55000) 不可行。需要优化。

    优化转移:采用完全背包式递推。
    对每个固定 ii,将 dpdp 看作二维数组(jj 为芯片数,kk 为价值)。
    转移实际是:对每个 j,kj,k,增加一个芯片在位置 ii

    dp[j+1][k+fi]+=dp[j][k]dp[j+1][k+f_i] \mathrel{+}= dp[j][k]

    并且这个操作可以进行任意次。
    这等价于对每个 ii 做二维完全背包:
    枚举 jj00n1n-1kk0055000fi55000 - f_i

    dp[j+1][k+fi]+=dp[j][k]dp[j+1][k+f_i] \mathrel{+}= dp[j][k]

    但注意这样会重复计算顺序?实际上因为我们按位置 ii 顺序处理,且对每个 ii 内部循环 jj 升序、kk 升序,就能保证每个 ii 的芯片数可以无限取,且芯片总数不断增加。这正是二维完全背包的标准写法。

    最终实现

    • 初始化 dp[0][0]=1dp[0][0] = 1
    • 对每个位置 i=1..xi = 1..x
      • jj00n1n-1
        • kk0055000fi55000 - f_i
          • dp[j+1][k+fi]+=dp[j][k]dp[j+1][k+f_i] += dp[j][k]
    • 最后答案 = v=055000[c[v]==m]×dp[n][v]\sum_{v=0}^{55000} [c[v] == m] \times dp[n][v]

    复杂度分析

    • 预处理 cc 数组:O(x55000)O(x \cdot 55000),可忽略。
    • DP:O(xn55000)O(x \cdot n \cdot 55000),即 10×1000×55000=5.5×10810 \times 1000 \times 55000 = 5.5 \times 10^8,在 C++ 中稍大但常数小且取模运算优化后可通过(实际实现加了一些剪枝和循环顺序优化)。
    • 空间:O(n55000)O(n \cdot 55000) 过大,但可以使用滚动数组优化 jj 维度?注意 jj 的转移依赖 jj 本身(完全背包),且 kk 必须用当前 ii 更新后的值,因此不能简单滚动 jj。但可以注意到 kk 很大而 nn 较小,实际实现中使用了二维数组 p[1003][60003]p[1003][60003],内存约 1003×60003×8480MB1003 \times 60003 \times 8 \approx 480\text{MB},稍大但仍在可接受范围(代码中使用 long long 存储模数,实际可改用 int 减少一半)。

    AC 代码

    #include<bits/stdc++.h>
    using namespace std;
    
    const int MOD = 998244353;
    const int MAXV = 55000;   // 最大可能总价值
    int f[25];                // 斐波那契数
    int c[MAXV + 5];          // c[v] = 拼出价值 v 所需的最小芯片数
    int dp[1005][MAXV + 5];   // dp[j][v] = 用 j 个芯片得到总价值 v 的方案数
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        // 预处理斐波那契数
        f[1] = f[2] = 1;
        for (int i = 3; i <= 24; i++) {
            f[i] = f[i-1] + f[i-2];
        }
    
        // 预处理 c[v]:最少芯片数
        memset(c, 0x3f, sizeof(c));
        c[0] = 0;
        for (int i = 1; i <= 10; i++) {          // 题目中 x ≤ 10
            for (int v = f[i]; v <= MAXV; v++) {
                if (c[v - f[i]] + 1 < c[v]) {
                    c[v] = c[v - f[i]] + 1;
                }
            }
        }
    
        int n, x, m;
        cin >> n >> x >> m;
    
        // DP 计数
        dp[0][0] = 1;
        for (int pos = 1; pos <= x; pos++) {
            int cost = f[pos];            // 在这个位置放一个芯片的价值
            // 完全背包:对每个位置,可以放任意多个芯片
            for (int chips = 0; chips < n; chips++) {
                for (int val = 0; val + cost <= MAXV; val++) {
                    if (dp[chips][val]) {
                        dp[chips + 1][val + cost] = (dp[chips + 1][val + cost] + dp[chips][val]) % MOD;
                    }
                }
            }
        }
    
        // 累加答案
        long long ans = 0;
        for (int v = 0; v <= MAXV; v++) {
            if (c[v] == m) {
                ans = (ans + dp[n][v]) % MOD;
            }
        }
        cout << ans << "\n";
    
        return 0;
    }
    • 1

    信息

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