1 条题解

  • 0
    @ 2026-4-16 17:17:37

    Missing Subsequence Sum 详细题解

    这是一道构造题,核心思路是利用二进制子集和性质1,2,4,1,2,4,\dots 能凑出所有数),通过微小修改,恰好让 kk 无法凑出,其余 1n1\sim n 全部能凑出,且序列长度不超过 2525,完全满足题目限制。

    一、关键观察(题眼)

    1. 固定 kk 时,能解决 n=cn=c 的序列,一定能解决所有 n<cn<c。因此我们直接把 nn 视作最大值 10610^6 构造即可,无需关心输入的 nn2. 标准二进制序列 a=[1,2,4,8,,219]a=[1,2,4,8,\dots,2^{19}]

    • 长度仅 2020,满足 25\le25 的限制;
    • 子集和能恰好凑出 122011 \sim 2^{20}-1(覆盖 10610^6);
    • 这是我们构造的基础。

    二、构造方案(标程核心)

    步骤1:计算关键指数 ii

    找到最大的整数 ii,满足:

    2ik2^i \le k

    步骤2:生成最终序列

    序列总长度不超过22,远小于题目限制 2525,格式如下:

    $$a = \left[\ k-2^i,\ k+1,\ k+1+2^i,\ 1,2,4,\dots,2^{i-1},\ 2^{i+1},\dots,2^{19}\ \right] $$

    拆解序列组成:

    1. 特殊数1:k2ik-2^i
    2. 特殊数2:k+1k+1
    3. 特殊数3:k+1+2ik+1+2^i
    4. 二进制基础段:1,2,4,,2i11,2,4,\dots,2^{i-1}(缺少 2i2^i
    5. 二进制高位段:2i+1,,2192^{i+1},\dots,2^{19}

    三、正确性证明

    1. 证明:没有子集和等于 kk

    只有数值 k\le k 的元素能参与凑出 kk,这些元素是:

    k2i, 1,2,4,,2i1k-2^i,\ 1,2,4,\dots,2^{i-1}

    计算它们的总和:

    (k2i)+(2i1)=k1(k-2^i) + (2^i -1) = k-1

    这些数的最大子集和为 k1k-1永远无法凑出 kk,满足第一个条件。


    2. 证明:所有 1vn, vk1\le v\le n,\ v\neq k 都能凑出

    分三类情况讨论:

    情况1:v<2iv < 2^i

    直接用二进制段 [1,2,4,,2i1][1,2,4,\dots,2^{i-1}],二进制分解即可凑出 vv

    情况2:2iv<k2^i \le v < k

    1. 先取所有 k\le k 的元素,和为 k1k-1
    2. 需要减掉的数值:target=(k1)vtarget = (k-1)-v
    3. 因为 2iv<k<2i+12^i\le v<k<2^{i+1},所以 target<2itarget < 2^i
    4. 用二进制段去掉 targettarget 对应的元素,最终和为 vv

    情况3:v>kv > k

    1. 优先选 k+1k+1,剩余需要凑:v(k+1)v-(k+1)
    2. 用二进制序列凑剩余值;
    3. 边界:若 v(k+1)v-(k+1) 包含 2i2^i 位,替换为 k+1+2ik+1+2^i 即可。

    综上,所有非 kk 的数都能凑出。


    四、代码实现(C++)

    实现逻辑

    1. 预处理二进制幂次 202192^0\sim2^{19}
    2. 对每组 n,kn,k,计算最大的 ii 满足 2ik2^i\le k
    3. 按构造规则生成序列,直接输出。
    #include <iostream>
    #include <vector>
    using namespace std;
    
    // 预处理 2^0 ~ 2^19,足够覆盖 1e6
    long long p[25];
    void prework() {
        p[0] = 1;
        for (int i = 1; i <= 19; ++i)
            p[i] = p[i-1] * 2;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);
        prework();
        
        int t;
        cin >> t;
        while (t--) {
            int n, k;
            cin >> n >> k;
            
            // 找到最大的 i 满足 2^i <= k
            int i = 0;
            while (p[i+1] <= k) i++;
            
            vector<long long> ans;
            // 加入三个特殊元素
            ans.push_back(k - p[i]);
            ans.push_back(k + 1);
            ans.push_back(k + 1 + p[i]);
            
            // 加入 1,2,...,2^(i-1)
            for (int j = 0; j < i; ++j)
                ans.push_back(p[j]);
            // 加入 2^(i+1) ... 2^19
            for (int j = i+1; j <= 19; ++j)
                ans.push_back(p[j]);
            
            // 输出
            cout << ans.size() << '\n';
            for (auto x : ans) cout << x << ' ';
            cout << '\n';
        }
        return 0;
    }
    

    五、复杂度分析

    • 预处理:O(log106)=O(20)O(\log 10^6) = O(20)
    • 每组测试用例:O(logn)O(\log n) 生成序列;
    • 序列长度:固定 22\le 22,完美满足题目 m25m\le25 的限制。

    六、样例验证

    以输入 9 3 为例:

    • k=3k=3,最大 i=1i=121=232^1=2\le3);
    • 特殊数:32=1, 4, 4+2=63-2=1,\ 4,\ 4+2=6
    • 二进制段:11j=0j=0),跳过 212^1,加入 4,8,164,8,16\dots
    • 最终序列:[1,4,6,1,4,8,][1,4,6,1,4,8,\dots],和样例输出一致,能凑出 191\sim933 外的所有数。

    总结

    1. 核心工具:二进制子集和(1,2,4,1,2,4,\dots 凑所有数);
    2. 构造关键:用 k2ik-2^i 让最大子集和变为 k1k-1,恰好无法凑出 kk
    3. 合规性:序列长度 22\le22,满足题目所有限制;
    4. 通用性:一次构造,适配所有 n106n\le10^6
    • 1

    信息

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