本文最后更新于 8 个月前,文中所描述的信息可能已发生改变。
D. Shohag Loves GCD 解题思路
核心问题分析
题目要求我们构造一个字典序最大的数组 ,使得对于任意 ,满足:
且所有 都必须来自给定的集合 。
1. 条件转化与数学推导
我们先考察最特殊的整除情况。 假设 (即 是 的因数),那么 。 此时题目条件转化为:
因为 一定是 的约数,要使得它不等于 ,唯一的办法就是 。这意味着 不能整除 。
推论: 如果在索引上存在整除关系 ,则在数值上必须满足 。
为了构造字典序最大的数组,我们希望前面的数尽可能大。对于 ,最简单且有效的满足 的方式是强制让 。 因为如果 ,显然 不可能整除 ( 均为正整数)。
2. 构造策略 (基于除数链的深度)
基于上述“若 则 ”的贪心策略,我们需要为每个下标 定义一个“层级”或“深度”。
定义 为以 结尾的最长整除链的长度。 即存在序列 使得 ,则 。 例如:
- (序列:1)
- (序列:1 -> 2)
- (序列:1 -> 2 -> 4)
- (序列:1 -> 2 -> 6 或 1 -> 3 -> 6)
贪心分配: 如果我们按照 的值来分配 中的元素:
- 越小(越接近根节点 1),分配 中越大的值。
- 越大(越接近末端),分配 中越小的值。
具体做法是:
- 计算所有 的 值。
- 将集合 从大到小排序。
- 令 (注意 也是 0-indexed)。
3. 正确性验证
我们验证一下这种构造是否满足题目原本的条件。 设 。显然 且 。
根据 的定义,整除关系意味着层级的增加,即 且 (当 且 时)。
由于我们将 降序排列并按 分配:
- 对应较小的 ,所以 的值较大。
- 和 对应较大的 ,所以值较小。
- 即 且 。
判断:
因为 严格大于 和 ,所以 必然严格大于 (因为 )。 所以 恒成立。
无解判定: 如果算出的最大深度 超过了集合 的大小 ,说明我们没有足够的数来区分这么长的整除链,此时输出 -1。
4. C++ 代码实现细节
代码中使用类似于埃氏筛(Sieve)的方法来快速计算所有数的 。
- 初始化所有 。
- 对于每个 ,更新其倍数 :。
- 这种递推保证了 正确记录了最长链。
CPP 代码实现
使用每个数字的深度去寻找它当前所能填入的最大数字。
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
int depth[MAXN];
// 预处理:计算每个数字的深度(最长除数链长度)
// 类似于埃氏筛,时间复杂度 O(N log N) 或 O(N log log N) 取决于具体实现,这里是调和级数求和 O(N log N)
void create() {
// 初始化深度为 1 (链: 1->i 本身至少长度为1,虽然题目中由1开始,但逻辑一致)
// 实际上这里 depth[1]=1, depth[2]=2...
for (int i = 1; i < MAXN; i++) depth[i] = 1;
for (int i = 1; i < MAXN; i++) {
// 枚举 i 的倍数,更新倍数的深度
for (int j = 2 * i; j < MAXN; j += i) {
depth[j] = max(depth[j], depth[i] + 1);
}
}
}
void solve() {
int data_number, data_object;
cin >> data_number >> data_object; // n 和 m
vector<int> data(data_number + 1);
vector<int> objects(data_number);
// 读入集合 S
for (int i = 0; i < data_object; i++) {
cin >> objects[i];
}
// 将 S 从大到小排序,以便贪心地将大数分配给低深度的位置
sort(objects.begin(), objects.end(), greater<int>());
for (int i = 1; i <= data_number; i++) {
// 如果当前数字需要的深度超过了集合 S 的大小,说明无法构造
// 因为一条长为 K 的除数链至少需要 K 个不同的数字
if (depth[i] > data_object) {
cout << -1 << endl;
return;
}
// 分配数值:深度越小,索引越小,值越大
data[i] = objects[depth[i] - 1];
}
// 输出结果
for (int i = 1; i <= data_number; i++) {
cout << data[i] << " ";
}
cout << endl;
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
// 预处理 depth 数组
create();
int t = 1;
cin >> t;
while (t--) {
solve();
}
}