题解
题目分析
我们需要构造一个长度为 的排列,使得恰好有
个不动点。不动点是指满足
的位置
。
解题思路
观察发现,当 时,无法构造满足条件的排列。原因如下:
- 假设有
个不动点,那么剩下的
个位置不能是不动点
- 但是剩下的
个位置只能放剩下的
个数,而这个数必然等于这个位置(因为其他位置都是不动点,剩下的数就是剩下的位置)
- 所以无法避免这个位置也成为不动点,导致总共有
个不动点
当 时,我们可以这样构造:
- 前
个位置设为不动点:
- 中间位置
不设为不动点,而是放一个较大的数
- 后面的位置
到
也设为不动点
- 最后将
放在最后的位置
这样构造后:
- 位置
到
:
,是不动点 ✅
- 位置
:
(如果
),不是不动点 ❌
- 位置
到
:
,不是不动点 ❌
- 位置
:
,当且仅当
时才是不动点
总的不动点数量恰好为 个。
代码解析
#include <bits/stdc++.h>
using namespace std;
#define f(i, a, b) for (ll i = a; i <= b; i++)
#define fr(i, a, b) for (ll i = a; i >= b; i--)
typedef long long ll;
const int N = 1e5 + 10;
const int Mod = 1e9 + 7;
void solves(){
ll n, m; // m 就是题目中的 k
cin >> n >> m;
ll cnt = 0; // 计数器,记录已经输出的元素个数
// 特判:当 n - m = 1 时,无法构造
if(n - m == 1) {
cout << -1;
} else {
// 1. 前 m 个位置设为不动点:1, 2, ..., m
f(i, 1, m) {
cout << i << " ";
cnt++;
}
// 2. 从 m+2 到 n 的位置也设为不动点
f(i, m + 2, n) {
cout << i << " ";
cnt++;
}
// 3. 如果还剩一个位置,放 m+1
if(n - cnt == 1) {
cout << m + 1;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T; // 题目只有一组测试数据
while (T--) {
solves();
}
return 0;
}
算法复杂度
- 时间复杂度:
,只需要遍历一次输出结果
- 空间复杂度:
,只使用了常数级额外空间
示例验证
示例1:
,可以构造
- 前
个位置:
- 从
到
:
- 剩余位置放
- 结果:
- 验证:位置
是不动点,共
个 ✓
示例2:
,可以构造
- 前
个位置:
- 从
到
,无输出
- 无剩余位置
- 结果:
- 验证:位置
都是不动点,共
个 ✓
无法构造的情况:
,无法构造
- 输出:
- 验证:确实无法构造恰好
个不动点的
元排列 ✓

京公网安备 11010502036488号