CodeForces 1882E2 Two Permutations (Hard Version)
如何评价,模拟赛搬了一道,前一天晚上代码写了一半的题。
考虑如何让操作次数最小。发现直接做太困难了。根本原因是,一次操作对序列的影响太大了。考虑做一些转化,减少一次操作对序列的影响。
仍然先考虑一个排列怎么做。
不知道为什么可以想到在排列前面添加特殊字符 \(0\) 变成 \(\{0, p_1, p_2, \ldots, p_n\}\),实际意义是真正的排列是从 \(0\) 的位置开始循环往右读形成的排列。我们每次操作相当于把 \(0\) 和一个数交换,目标是把排列变成 \(\{0, 1, 2, \ldots, n\}, \{n, 0, 1, \ldots, n - 1\}, \{n - 1, n, 0, \ldots, n - 2\}\) 等等其中之一。不失一般性,设要变成 \(\{0, 1, 2, \ldots, n\}\),若不是可以把排列重编号。
这样就好办了。我们连边 \(i \to p_i\),转化成置换环考虑。设此时排列的特殊字符为 \(x = p_0\)(因为重标号过了,所以特殊字符不一定是 \(0\))。设 \(q_i\) 为 \(i\) 的前驱,等价于 \(q_{p_i} = i\)。那么此时我们可以选择任意一个点,把它和 \(q_x\) 的出边(\(p\) 值)交换。
我们首先处理 \(x\) 所在的环。先把 \(q_x\) 和 \(q_{q_x}\) 交换,这样 \(q_x\) 会变成自环,原来的 \(q_{q_x}\) 变成了现在的 \(q_x\)。不断这样操作直到 \(x\) 变成自环。
然后我们操作不包含 \(x\) 的环。显然自环不需要被操作,否则设环上任意一点编号为 \(i\)。先把 \(x\) 和 \(i\) 交换,相当于把这个环塞入 \(x\),转成包含 \(x\) 的环的情况做。
设环长分别为 \(l_1, l_2, \ldots, l_k\),编号为 \(1\) 的环是 \(x\) 所在的环。那么操作次数是:
枚举目标排列是 \(\{0, 1, 2, \ldots, n\}, \{n, 0, 1, \ldots, n - 1\}, \{n - 1, n, 0, \ldots, n - 2\}\) 等等的其中一个,取最小值即可。
考虑两个排列。对两个排列分别做一遍上面的过程。对于奇偶性相同的两个操作序列,我们仍然可以不断往短的操作序列补 \(1, n\) 直到它们长度相等。对于奇偶性不同的两个操作序列,因为我们每种目标排列都考虑过了,所以它们不优。那只需要分奇偶性求出每个排列的最短奇数次操作和最短偶数次操作,取最小值即可。
构造直接按上面的模拟即可。
时间复杂度 \(O(n^2)\)。
code
// Problem: E2. Two Permutations (Hard Version)
// Contest: Codeforces - Codeforces Round 899 (Div. 2)
// URL: https://codeforces.com/problemset/problem/1882/E2
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 2510;
int n, m, a[maxn], b[maxn], p[maxn], q[maxn];
bool vis[maxn];
inline int calc(int *a, int n, int pos) {
mems(vis, 0);
for (int i = 0; i <= n; ++i) {
p[i] = (a[i] + pos) % (n + 1);
}
int ans = 0;
for (int i = 0; i <= n; ++i) {
if (vis[i]) {
continue;
}
int u = i, cnt = 0;
do {
vis[u] = 1;
u = p[u];
++cnt;
} while (u != i);
if (i == 0) {
ans += cnt - 1;
} else if (cnt > 1) {
ans += cnt + 1;
}
}
return ans;
}
inline void oper(int x, int y) {
int u = p[x], v = p[y];
swap(p[x], p[y]);
swap(q[u], q[v]);
}
inline void work(int *a, int n, int pos, vector<int> &S) {
mems(vis, 0);
for (int i = 0; i <= n; ++i) {
p[i] = (a[i] + pos) % (n + 1);
}
for (int i = 0; i <= n; ++i) {
q[p[i]] = i;
}
int x = 0;
while (x != p[x]) {
int t = q[x];
vis[x] = vis[t] = 1;
oper(x, t);
S.pb((t - x + n + 1) % (n + 1));
x = t;
}
for (int i = 0; i <= n; ++i) {
if (p[i] == i || vis[i]) {
continue;
}
oper(x, i);
S.pb((i - x + n + 1) % (n + 1));
x = i;
while (x != p[x]) {
int t = q[x];
vis[x] = vis[t] = 1;
oper(x, t);
S.pb((t - x + n + 1) % (n + 1));
x = t;
}
}
}
void solve() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= m; ++i) {
scanf("%d", &b[i]);
}
int ap1 = -1, ap2 = -1, bp1 = -1, bp2 = -1;
int am1 = 2e9, am2 = 2e9, bm1 = 2e9, bm2 = 2e9;
for (int i = 0; i <= n; ++i) {
int t = calc(a, n, i);
if (t & 1) {
if (t < am1) {
am1 = t;
ap1 = i;
}
} else {
if (t < am2) {
am2 = t;
ap2 = i;
}
}
}
for (int i = 0; i <= m; ++i) {
int t = calc(b, m, i);
if (t & 1) {
if (t < bm1) {
bm1 = t;
bp1 = i;
}
} else {
if (t < bm2) {
bm2 = t;
bp2 = i;
}
}
}
int ans = min(max(am1, bm1), max(am2, bm2));
if (ans > 1e9) {
puts("-1");
return;
}
vector<int> v1, v2;
if (ans == max(am1, bm1)) {
work(a, n, ap1, v1);
work(b, m, bp1, v2);
} else {
work(a, n, ap2, v1);
work(b, m, bp2, v2);
}
while (v1.size() < v2.size()) {
v1.pb(1);
v1.pb(n);
}
while (v2.size() < v1.size()) {
v2.pb(1);
v2.pb(m);
}
printf("%d\n", (int)v1.size());
for (int i = 0; i < (int)v1.size(); ++i) {
printf("%d %d\n", v1[i], v2[i]);
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}