题解 P8390【[COI2021] MalnaRISC】
有趣的构造题。前段时间研究过排序网络,然后随机跳题跳到了这题,就来写了。
可以发现本题即要求构造一个足够优秀的排序网络。
不妨设
下文中规定区间
定义一个序列是 双调 的,当且仅当它先增后减或者先减后增。
Batcher 曾提出一个比较网络,用于将一个长度为
接下来考虑利用双调归并网络进行排序,我们需要解决输入不是双调序列的情形。显然长度为
对序列 4 6 2 7 5 1 3 8 的每一次操作后结果如下:
INIT | 4 6 2 7 5 1 3 8
OP 1 | 4 6 2 7 1 5 3 8
OP 2 | 4 2 6 7 1 3 5 8
OP 3 | 2 4 6 7 1 3 5 8
OP 4 | 2 4 3 1 7 6 5 8
OP 5 | 2 1 3 4 5 6 7 8
OP 6 | 1 2 3 4 5 6 7 8
每一个双调归并网络的操作数为
参考代码:
//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x,y,z) for(int x=(y);x<=(z);x++)
#define per(x,y,z) for(int x=(y);x>=(z);x--)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do{freopen(s".in","r",stdin);freopen(s".out","w",stdout);}while(false)
using namespace std;
typedef long long ll;
int n;
vector<vector<tuple<int, int>>> ans;
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
int main() {
scanf("%d", &n);
int m = 1;
for(; m < n; m <<= 1);
for(int l = 2; l <= m; l <<= 1) {
int k = ans.size();
ans.push_back({});
for(int p = 1; p <= m; p += l) {
for(int i = p, j = p + l - 1; i < j; i++, j--) {
ans[k].emplace_back(i, j);
}
}
for(int o = l >> 1; o >= 2; o >>= 1) {
int k = ans.size();
ans.push_back({});
for(int p = 1; p <= m; p += o) {
for(int i = p, j = p + (o >> 1); i < p + (o >> 1) && j < p + o; i++, j++) {
ans[k].emplace_back(i, j);
}
}
}
}
printf("%d\n", (int)ans.size());
for(auto& i : ans) {
for(auto& j : i) {
int x = get<0>(j), y = get<1>(j);
if(x > n || y > n) continue;
printf("CMPSWP R%d R%d ", get<0>(j), get<1>(j));
}
puts("");
}
return 0;
}
参考文献:2002 年国家候选队论文 符文杰《排序网络》。