1 条题解

  • 0
    @ 2026-5-4 18:19:59

    CF1988C Increasing Sequence with Fixed OR 题解

    题意简述

    给定正整数 nn,要求构造一个严格递增的整数序列 a1<a2<<aka_1 < a_2 < \dots < a_k,满足:

    • aina_i \le n
    • 对于所有 2ik2 \le i \le k,有 aiai1=na_i \mid a_{i-1} = n(按位或)。

    求序列的最大长度 kk,并输出任意一个最长序列。n1018n \le 10^{18},多组数据,序列总长度 5×105\le 5 \times 10^5


    核心观察:按位或的约束

    条件 aiai1=na_i \mid a_{i-1} = n 意味着:对于 nn 的二进制表示中每一个为 11 的位,aia_iai1a_{i-1}至少有一个在该位上为 11;而对于 nn 中为 00 的位,两者必须都为 00

    换言之,所有 aia_i 的二进制表示必须是 nn子集(即 ai  &  n=0a_i \;\&\; \sim n = 0),且相邻两项必须共同覆盖 nn 中所有的 11 位。


    构造最长序列

    由于 aia_inn 的子集,且序列需严格递增,我们可以从高位到低位消去 11 来依次生成前驱。

    具体方法

    1. 令最后一个元素 ak=na_k = n(显然最大)。
    2. 每次选取当前 nn最低位的一个 11,将它变为 00,得到一个新的数作为前一个元素。
    3. 重复直至数字变为 00,结束。

    例如 n=23=(10111)2n = 23 = (10111)_2

    步骤 当前值 消去最低位 11 得到
    1 (10111)2=23(10111)_2 = 23 (10110)2=22(10110)_2 = 22
    2 (10110)2=22(10110)_2 = 22 (10101)2=21(10101)_2 = 21
    3 (10101)2=21(10101)_2 = 21 (10011)2=19(10011)_2 = 19
    4 (10011)2=19(10011)_2 = 19 (00111)2=7(00111)_2 = 7
    5 (00111)2=7(00111)_2 = 7 (00000)2=0(00000)_2 = 0 —— 停止

    倒序输出即为:[7,19,21,22,23][7, 19, 21, 22, 23]


    为什么这样是最长的?

    nn 的二进制中 11 的个数为 cc。每个 aia_i 都是 nn 的子集(即位 11 的集合是 nn11 的子集)。

    考虑序列的二进制包含关系:由于 ai<ai+1a_i < a_{i+1}(严格递增),且 ai+1ai=na_{i+1} \mid a_i = n,这意味着 ai+1a_{i+1} 必须比 aia_i “多出”某些 11 位(至少一个)。而所有 11 位的总数只有 cc 个,因此序列长度不可能超过 cc(包括 nn 本身)。

    上述构造方法恰好每步消去一个 11,序列长度达到 cc,故为最长。


    Lowbit 实现

    利用 lowbit(x) = x & -x 提取最低位的 11,循环消去即可。

    时间复杂度:O(Tlogn)O(T \log n),空间 O(logn)O(\log n)


    参考代码

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        int T;
        cin >> T;
        while (T--) {
            ll n;
            cin >> n;
    
            vector<ll> ans = {n};
            for (ll m = n; m; ) {
                ll t = m & -m;          // lowbit
                m -= t;
                if (n - t > 0)
                    ans.push_back(n - t);
            }
    
            // ans 中存储的是从大到小的顺序,需要逆序输出
            reverse(ans.begin(), ans.end());
            cout << ans.size() << '\n';
            for (ll x : ans) cout << x << ' ';
            cout << '\n';
        }
        return 0;
    }
    • 1

    信息

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