Content
给定 \(n,m\),你需要输出一个长度为 \(n\) 的排列,满足该排列进行不超过 \(m\) 次交换操作可以得到的最小的字典序最大。
数据范围:\(T\) 组数据,\(1\leqslant T\leqslant 10^5\),\(1\leqslant n\leqslant 10^5\),\(\sum n\leqslant 10^5\),\(0\leqslant m\leqslant n\)。
Solution
算是一道比较小清新的构造题,接下来教你如何弄出正确的构造方案。
首先,要如何分配这 \(m\) 次交换,使得 \(m\) 次交换后的数列字典序最小?经分析不难得知,第 \(i\) 次交换的时候就应该把数字 \(i\) 和第 \(i\) 个位置上的数字进行交换,才能够达到最小字典序的目的。
那么如何构造出数列使得 \(m\) 次交换后的数列的最小字典序最大?既然 \(m\) 次交换后前面交换完的数已经是字典序最小了,那么就应当使得后面的部分的字典序最大。同时,为了尽可能多地消耗交换次数,不应该将数字 \(i(1\leqslant i\leqslant m)\) 放在第 \(i\) 个位置上面。
那么构造方案就呼之欲出了:\(\{n,1,2,\dots,m,n-1,n-2,\dots,m+1\}\)。这样构造既可以保证前 \(m\) 个要交换的数字不在自己的数字所表示的位置上面,又可以在 \(m\) 次交换之后使得最小字典序最大。因为后面的数不用进行交换,我们就先按照字典序最大给它排列好了,这样交换完以后后面的 \(n-m\) 个数字就一定可以保证是字典序最大的(前 \(m\) 个数字已经确定是 \(1,2,\dots,m\) 了)。
Code
namespace Solution {
const int N = 1e5 + 7;
int n, m;
iv Main() {
MT {
read(n, m);
if(n == m) F(int, i, 1, n) printf("%d%c", i, " \n"[i == n]);
else {
printf("%d", n);
F(int, i, 1, m) printf(" %d", i);
R(int, i, n - 1, m + 1) printf(" %d", i);
puts("");
}
}
return;
}
}