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

Kinandra
:D搬运于
2025-08-24 22:15:03,当前版本为作者最后更新于2020-05-12 15:06:15,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
标签: 构造
Part 1
设
E的一次操作为 ,A的一次操作为 , 那么最终的操作序列应该为 .发现一组 操作必定可以转化为一组 , 意思是存在一个
E先操作一步,A再操作一步的效果与A先操作一步,E再操作一步的效果相同, 并且对于确定的 , 和 是一一对应的.于是我们不妨把所有 移动到 的后面, 构造一个这样的操作序列 , 并通过这个序列还原出原序列.
Part 2
很容易构造, 我们只需要二分答案, 然后让
E先把操作走完, 求一下此时使排列有序的操作次数( ) 并随便构造一下 序列即可.考虑从 如何还原为 , 对于已知的 考虑使 的 , 有如下几种情况:
- , 则 .
- , 则 .
- , 则 .
不难发现: 还原到 , 如果 与 中有相同的位置, 那么就把这个位置变成 中的另一个位置.
Part 3
暴力还原原操作序列是 的(一个 需要与 个 交换), 不能承受, 考虑优化.
发现 与 交换等价于把 映射到 , 映射到 , 并且这个映射是可以合并的. 于是的到这样一个算法: 从后向前扫 序列, 边维护当前 后缀对应的映射, 边计算出插在当前 前面的 (详见代码). 这个部分的复杂度是 .
时间复杂度 .
#include <bits/stdc++.h> using namespace std; int read(); int n, flg, a[200005]; int m, x[200005], y[200005]; int b[200005]; bool vis[200005]; void get(int t) { for (int i = 0; i < n; ++i) b[i] = a[i]; for (int i = 1; i <= t; ++i) swap(b[x[i]], b[y[i]]); } bool check(int x) { get(x), memset(vis, 0, n); int res = n; for (int i = 0; i < n; ++i) { if (!vis[i]) { --res; for (int j = i; !vis[j]; j = b[j]) vis[j] = 1; } } return res <= x; } vector<int> res; void solve(int x) { get(x), memset(vis, 0, n); for (int i = 0; i < n; ++i) if (!vis[i] && b[i] != i) for (int j = i; !vis[b[j]]; j = b[j]) vis[j] = 1, res.push_back(j); while (res.size() < x) res.push_back(n); } int p[200005], fp[200005], rx[200005], ry[200005]; int main() { n = read(); for (int i = 0; i < n; ++i) a[i] = read(), flg |= (a[i] != (p[i] = fp[i] = i)); if (!flg) return puts("0"), 0; m = min(read(), n - 1); for (int i = 1; i <= m; ++i) x[i] = read(), y[i] = read(), x[i] > y[i] ? swap(x[i], y[i]) : void(); int l = 1, r = m - 1, rs = r + 1, mid; while (l <= r) check(mid = l + r >> 1) ? r = (rs = mid) - 1 : l = mid + 1; printf("%d\n", rs), solve(rs); for (int i = 0; i < rs; ++i) { res[i] < n ? rx[i] = p[res[i]], ry[i] = p[b[res[i]]] : 0; swap(p[fp[x[rs - i]]], p[fp[y[rs - i]]]); swap(fp[x[rs - i]], fp[y[rs - i]]); } for (int i = 1; i <= rs; ++i) printf("%d %d\n", rx[rs - i], ry[rs - i]); return 0; } int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c > '9') f = (c == '-') ? -1 : f, c = getchar(); while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * f; }
- 1
信息
- ID
- 4858
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者