1 条题解

  • 0
    @ 2026-5-14 14:32:31

    题目理解

    我们有一个初始集合 SS,包含 nn 个互不相同的正整数。
    初始分数为 11
    每次操作:

    • m=min(S)m = \min(S)
    • 分数 ×m\times m
    • SS 中删除 mm
    • 1,2,,m11,2,\dots,m-1 全部加入 SS(不会产生重复)

    执行恰好 kk 次操作(保证集合非空),求最终分数对 109+710^9+7 取模。


    第一步:转化为二进制表示

    为了方便,先将所有数减 11(因为操作中涉及 11m1m-1,减 11 后变为 00m2m-2)。
    设集合 SS 对应一个二进制数:

    f(S)=vS2vf(S) = \sum_{v \in S} 2^v

    操作效果

    • 找到最低位 bb(即最小值 m=b+1m = b+1
    • b\le b 的所有位翻转(因为 00bb 这些数要么被删除,要么被加入)
    • 实际上,这正是二进制数减 11 的操作:
      最低位 bb1100,所有更低位从 0011

    因此:

    f(S)=f(S)1f(S') = f(S) - 1

    重要结论

    • 每次操作,f(S)f(S) 减少 11
    • f(S)f(S)f(S)2mf(S) - 2^m 需要 2m2^m 步,因为要逐次减 11 经过中间值。

    特别地,要将最小值 mm(即二进制第 m1m-1 位)完全移除,需要恰好 2m12^{m-1} 次操作?
    等一下,注意:如果最小值是 mm(原始值),对应二进制位是 m1m-1
    那么 f(S)f(S) 包含 2m12^{m-1} 这一项。
    要使这一位变为 00,需要从 2m12^{m-1} 一直减到 00,共 2m12^{m-1} 步。
    但题解中写的是 2v2^v,其中 v=m1v = m-1,所以是一致的。


    第二步:计算 F(v)F(v) —— 完整移除最小值 v+1v+1 的贡献

    F(v)F(v) 表示:当最小值为 v+1v+1(即二进制位 vv11)时,执行 2v2^v 次操作(刚好移除这个最小值)所累积的分数乘积。

    模拟过程:

    • 第一次操作:最小值 v+1v+1 被移除,分数乘 v+1v+1,然后加入 0,1,,v10,1,\dots,v-1
    • 之后,这些新加入的数会依次作为最小值被移除。
      移除顺序:先移除 00(需要 20=12^0=1 步,贡献 F(0)=1F(0)=1 倍),再移除 11(需要 21=22^1=2 步,贡献 F(1)F(1)),依此类推。

    实际上,移除 vv 的过程是:
    执行一次操作(乘 v+1v+1),然后递归处理 00v1v-1 这些更小的数。

    因此:

    F(0)=1F(0) = 1 F(v)=(v+1)j=0v1F(j)F(v) = (v+1) \cdot \prod_{j=0}^{v-1} F(j)

    这个递推可以直接预处理到 v=30v=30(因为 230>1092^{30} > 10^9kk 最大 10910^9)。


    第三步:模拟 kk 次操作

    将初始 SS 中的每个数减 11 后排序。
    设当前要处理的最小值为 vv(原始 v+1v+1)。

    情况 1:k2vk \ge 2^v

    我们可以完整移除这个最小值,消耗 2v2^v 步,分数乘 F(v)F(v)kk 减少 2v2^v,然后继续处理下一个最小值。

    情况 2:k<2vk < 2^v

    此时无法完全移除 vv。我们需要计算:当前最小值为 vv,还剩 kk 步,最终乘积是多少?
    定义 P(v,k)P(v, k) 为:当 SS 中只有 vv 和所有小于 vv 的数(且 vv 是当前最小值),剩余 kk 步(k<2vk < 2^v)时的乘积。

    边界P(v,0)=1P(v, 0) = 1

    转移
    第一步必须操作一次,乘 v+1v+1,然后 vv 被移除,集合变成包含 0,1,,v10,1,\dots,v-1
    之后,我们从 w=0w=0 开始依次处理这些新元素(因为它们会依次成为最小值):

    • 如果 k2wk \ge 2^w:可以完整移除 ww,乘 F(w)F(w)kk 减少 2w2^w
    • 否则 k<2wk < 2^w:进入子问题 P(w,k)P(w, k),然后停止(因为更大 ww 不会影响)。

    注意:kk 在第一步后已经减 11

    所以:

    $$P(v, k) = (v+1) \cdot \text{process\_children}(v-1, k-1) $$

    其中 process_children(v,k)\text{process\_children}(v, k) 表示从 w=0w=0vv 依次处理。

    由于 230>1092^{30} > 10^9,当 w30w \ge 30 时,2w>k2^w > k 必然成立,所以递归深度不超过 3030


    第四步:算法实现

    1. 预处理 F[0..30]F[0..30]

    2. 对每个测试用例:

      • 读入 n,kn,k,将 sis_i11,排序。
      • 遍历 ss
        • v30v \le 30k2vk \ge 2^vk=2vk -= 2^vans=F[v]ans *= F[v]
        • 否则:ans=P(v,k)ans *= P(v, k),跳出循环。
      • 输出 ansans
    3. P(v,k)P(v, k) 递归实现:

      • k==0k == 0 返回 11
      • ans=v+1ans = v+1kk--
      • w=0w = 0v1v-1(注意 vv 本身已被移除):
        • k2wk \ge 2^wk=2wk -= 2^wans=F[w]ans *= F[w]
        • 否则:ans=P(w,k)ans *= P(w, k),返回 ansans
      • 返回 ansans

    第五步:复杂度

    • 预处理 FFO(302)O(30^2)
    • 每个测试用例:排序 O(nlogn)O(n \log n)
    • 递归 PP:每次最多 3030 层,每层循环 O(30)O(30),总 O(302)O(30^2)
    • 总复杂度 O(nlogn+t900)O(\sum n \log n + t \cdot 900),满足约束。

    最终代码(C++,与标程一致)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int MOD = 1e9 + 7;
    long long F[31];
    
    long long P(int v, long long k) {
        if (k == 0) return 1;
        long long res = v + 1;
        k--;
        for (int w = 0; w < v; w++) {
            if (k >= (1LL << w)) {
                k -= (1LL << w);
                res = res * F[w] % MOD;
            } else {
                res = res * P(w, k) % MOD;
                break;
            }
        }
        return res;
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        
        F[0] = 1;
        for (int i = 1; i <= 30; i++) {
            F[i] = i + 1;
            for (int j = 0; j < i; j++) {
                F[i] = F[i] * F[j] % MOD;
            }
        }
        
        int t;
        cin >> t;
        while (t--) {
            int n;
            long long k;
            cin >> n >> k;
            vector<int> s(n);
            for (int i = 0; i < n; i++) {
                cin >> s[i];
                s[i]--;
            }
            sort(s.begin(), s.end());
            
            long long ans = 1;
            for (int i = 0; i < n; i++) {
                int v = s[i];
                if (v <= 30 && k >= (1LL << v)) {
                    k -= (1LL << v);
                    ans = ans * F[v] % MOD;
                } else {
                    ans = ans * P(v, k) % MOD;
                    break;
                }
            }
            cout << ans << '\n';
        }
        
        return 0;
    }
    
    • 1

    信息

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