1 条题解
-
0
题目理解
我们有一个初始集合 ,包含 个互不相同的正整数。
初始分数为 。
每次操作:- 令
- 分数
- 从 中删除
- 将 全部加入 (不会产生重复)
执行恰好 次操作(保证集合非空),求最终分数对 取模。
第一步:转化为二进制表示
为了方便,先将所有数减 (因为操作中涉及 到 ,减 后变为 到 )。
设集合 对应一个二进制数:操作效果:
- 找到最低位 (即最小值 )
- 将 的所有位翻转(因为 到 这些数要么被删除,要么被加入)
- 实际上,这正是二进制数减 的操作:
最低位 从 变 ,所有更低位从 变 。
因此:
重要结论:
- 每次操作, 减少 。
- 从 到 需要 步,因为要逐次减 经过中间值。
特别地,要将最小值 (即二进制第 位)完全移除,需要恰好 次操作?
等一下,注意:如果最小值是 (原始值),对应二进制位是 。
那么 包含 这一项。
要使这一位变为 ,需要从 一直减到 ,共 步。
但题解中写的是 ,其中 ,所以是一致的。
第二步:计算 —— 完整移除最小值 的贡献
设 表示:当最小值为 (即二进制位 为 )时,执行 次操作(刚好移除这个最小值)所累积的分数乘积。
模拟过程:
- 第一次操作:最小值 被移除,分数乘 ,然后加入 。
- 之后,这些新加入的数会依次作为最小值被移除。
移除顺序:先移除 (需要 步,贡献 倍),再移除 (需要 步,贡献 ),依此类推。
实际上,移除 的过程是:
执行一次操作(乘 ),然后递归处理 到 这些更小的数。因此:
这个递推可以直接预处理到 (因为 , 最大 )。
第三步:模拟 次操作
将初始 中的每个数减 后排序。
设当前要处理的最小值为 (原始 )。情况 1:
我们可以完整移除这个最小值,消耗 步,分数乘 , 减少 ,然后继续处理下一个最小值。
情况 2:
此时无法完全移除 。我们需要计算:当前最小值为 ,还剩 步,最终乘积是多少?
定义 为:当 中只有 和所有小于 的数(且 是当前最小值),剩余 步()时的乘积。边界:。
转移:
第一步必须操作一次,乘 ,然后 被移除,集合变成包含 。
之后,我们从 开始依次处理这些新元素(因为它们会依次成为最小值):- 如果 :可以完整移除 ,乘 , 减少 。
- 否则 :进入子问题 ,然后停止(因为更大 不会影响)。
注意: 在第一步后已经减 。
所以:
$$P(v, k) = (v+1) \cdot \text{process\_children}(v-1, k-1) $$其中 表示从 到 依次处理。
由于 ,当 时, 必然成立,所以递归深度不超过 。
第四步:算法实现
-
预处理 。
-
对每个测试用例:
- 读入 ,将 减 ,排序。
- 遍历 :
- 若 且 :,。
- 否则:,跳出循环。
- 输出 。
-
递归实现:
- 若 返回 。
- ,。
- 对 到 (注意 本身已被移除):
- 若 :,。
- 否则:,返回 。
- 返回 。
第五步:复杂度
- 预处理 :
- 每个测试用例:排序
- 递归 :每次最多 层,每层循环 ,总
- 总复杂度 ,满足约束。
最终代码(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
- 上传者