1 条题解
-
0
自动搬运
来自洛谷,原作者为

xlpg0713
**搬运于
2025-08-24 22:23:20,当前版本为作者最后更新于2024-06-18 10:21:14,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
给定长为 的序列 ,一次操作可以指定 ,将 中最大值与最小值调换位置,在 次操作以内将 变为另一指定序列。
首先要发现这个东西是可逆的,意思是做两次相同的操作不会改变这个序列。也即:如果一个序列经过若干次操作后达到一个状态,将操作序列翻转可以还原到原序列。
于是考虑找到一种操作方式将原序列排序,就解决了这个问题。
至于排序方法,考虑进行一个类似归并的分治。问题在于如何合并两个已经有序的序列。
如果有两个不相交的序列,可以用这种方式来排序:先将两个序列各自翻转,拼接成为一个下降序列,然后翻转回来。代价是 。

于是就可以对值域分治,过程形如这样:

上半部分和下半部分是一个子问题。
于是就可以实现排序,分治地做下去即可。
分析一下复杂度,最外层的分治有 层,对值域分治聪明一点也是 层,合并两个不相交的序列,一层的代价总和是 ,于是需要 次操作,能看出来常数是挺小的,可以过。
#include<iostream> #include<algorithm> #include<vector> const int N = 5005; int n, a[N], b[N]; std::vector<std::pair<int, int>> rs, rs; void ad(int l, int r){rs.push_back({l, r}); std::swap(a[l], a[r]);} void mg(int l, int r){ int f = 0, md = l, p1 = l, p2 = r; for(int i = l; i < r; i++) f |= (a[i] > a[i + 1]); if(f == 0) return; while(a[md] < a[md + 1]) md++; for(int i = l; i <= r; i++) b[i] = a[i]; std::nth_element(b + l, b + md, b + r + 1); while(a[p1] <= b[md]) p1++; while(a[p2] > b[md]) p2--; for(int i = p1, j = md; i < j; i++, j--) ad(i, j); for(int i = md + 1, j = p2; i < j; i++, j--) ad(i, j); for(int i = p1, j = p2; i < j; i++, j--) ad(i, j); mg(l, md), mg(md + 1, r); } void divide(int l, int r){ if(l == r) return; int md = l + r >> 1; divide(l, md), divide(md + 1, r), mg(l, r); } int main(){ std::ios::sync_with_stdio(false); std::cin.tie(0),std::cout.tie(0); std::cin >> n; for(int i = 1; i <= n; i++) std::cin >> a[i]; divide(1, n); rs = rs; rs.clear(); for(int i = 1; i <= n; i++) std::cin >> a[i]; divide(1, n); std::reverse(rs.begin(), rs.end()); for(auto x:rs) rs.push_back(x); std::cout << rs.size() << '\n'; for(auto [l, r] : rs) std::cout << l << ' ' << r << '\n'; }
- 1
信息
- ID
- 5724
- 时间
- 2000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者