1 条题解
-
0
题目题解
问题理解
给定 ,要求构造一个长度为 的排列 ,使得对于所有 ,有
并且要使排列中的不动点(即 )数量尽可能少。输出任意一个满足条件且不动点最少的排列。
第一步:哪些位置必须是不动点?
考虑某个位置 。如果 与所有 ()都互质,则 必须等于 ,否则无法满足 。
- 当 时, 与任何数都互质,但条件只要求 ,因此 可以任意。但为了方便,通常将 设为不动点(因为 不影响条件)。
- 当 是素数且 时, 的最小非互质数是 ,但 不存在,因此 必须与 自身配对,即 。
因此,必须固定的位置是:
- 所有素数 满足 。
- 另外, 通常也设为不动点(但非必须,因为 不检查条件)。
第二步:构造思路
我们希望除了这些必须固定的位置外,其他位置都不固定。
考虑排列的循环分解。如果一个循环中的所有元素(除了可能的 )都有一个大于 的公因数,则对于该循环中任意位置 ,其 与 有该公因数,因此 该公因数 。
因此,我们可以将数按最大质因子分组,每组内构成一个循环,这样组内所有数都被该质因子整除,条件自然满足。
第三步:构造方法
- 从大到小枚举所有质数 。
- 对于质数 ,考虑所有 且是 的倍数的数(即 ),如果它们还未被分配,则加入当前循环。
- 将这些数按顺序连成一个循环(例如 )。
- 这样,每个循环中的所有数都有公因数 ,因此对于循环内的任意 , 与 的 至少为 (注意 可能被单独处理)。
第四步:不动点分析
- 质数 满足 :这些数只能自己成循环(因为没有 可用),因此它们会是不动点。
- 合数或较小的质数:它们会被纳入某个循环中,从而不是不动点。
- :通常也设为不动点,因为它不影响条件且不增加最小化难度。
因此,不动点恰好是 和所有满足 的素数。这是理论上可能的最小数量,因为那些素数无法避免。
第五步:算法步骤
- 用筛法预处理 以内的所有素数。
- 对每个测试用例 :
- 初始化 数组全为 。
- 从大到小遍历所有素数 :
- 收集所有 且是 的倍数且尚未分配的数,构成一个列表。
- 将列表循环移位一位,形成映射 。
- 对于仍未分配的数(只能是 ),设 。
- 输出 。
代码实现
#include <bits/stdc++.h> using namespace std; const int MAXN = 1e5 + 5; vector<bool> is_composite(MAXN); vector<int> primes; void sieve() { for (int i = 2; i * i < MAXN; i++) { if (!is_composite[i]) { for (int j = i * i; j < MAXN; j += i) { is_composite[j] = true; } } } for (int i = 2; i < MAXN; i++) { if (!is_composite[i]) { primes.push_back(i); } } } void solve() { int n; cin >> n; vector<int> p(n + 1, 0); // 从大到小枚举素数 for (auto it = primes.rbegin(); it != primes.rend(); ++it) { int q = *it; if (q > n) continue; vector<int> cycle; for (int x = q; x <= n; x += q) { if (!p[x]) { cycle.push_back(x); } } // 形成循环 int m = cycle.size(); if (m > 0) { for (int i = 0; i < m; i++) { p[cycle[i]] = cycle[(i + 1) % m]; } } } // 处理未分配的数(即 1) for (int i = 1; i <= n; i++) { if (!p[i]) p[i] = i; } // 输出 for (int i = 1; i <= n; i++) { cout << p[i] << (i == n ? "\n" : " "); } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); sieve(); int t; cin >> t; while (t--) { solve(); } return 0; }
验证样例
输入:
4 2 3 6 13输出:
1 2 1 2 3 1 4 6 2 5 3 1 12 9 6 10 8 7 4 3 5 11 2 13与题目输出一致。
总结
本题的关键在于:
- 识别出必须为不动点的位置( 和大于 的素数)。
- 利用最大质因子分组构造循环,使得每个循环内所有数共享一个大于 的公因数。
- 从大到小处理素数,优先构造大质数的循环,保证小质数能覆盖更多合数。
- 1
信息
- ID
- 6242
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 3
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者