CF1885E2 做题记录
很厉害的构造题。link
先考虑 E1,手摸发现依次操作 \(a_x, a_y, a_x\) 可以实现 \(a_x\) 和 \(a_y\) 的交换而不影响其他数。
然后需要合并两个排列的方案。如果两个排列的操作数奇偶性相同,在操作数较小的方案后面加上若干个 \(1, n\) 即可。
如果奇偶性不同,若 \(n, m\) 中有一个为奇数,不妨设 \(n\) 为奇数,那么在第一个排列的方案后面加 \(n\) 个 \(1\) 即可转化为奇偶性相同的情况。
如果 \(n, m\) 都是偶数,推一下式子容易发现每次操作逆序对数量奇偶性一定改变,则此时无解。
考虑 E2,即需要最小化操作数。
原来的操作方法很抽象,很难描述最小操作数,所以需要转化。
我们在排列 \(p = \{1, 2, 3, \dots, n\}\) 前面加一个 \(0\),即 \(p_0 = 0\),并令 \(p\) 呈环形(即首尾相连),开头位置为 \(0\) 的位置。
不难发现,每次操作数字 \(x\) 时,相当于在 \(p\) 中将 \(0\) 和 \(x\) 交换。
最终合法的排列为 \(\{0, 1, 2, 3, \dots, n\}, \ \{n, 0, 1, 2, \dots, n - 1\}, \ \{n - 1, n, 0, 1, \dots, n - 2\}, \dots\)。一共有 \(n + 1\) 种可能,枚举每一种情况,相当于给每个数加上一个数 \(x_0\),最终状态为 \(\{0, 1, 2, 3, \dots, n\}\)。
相当于有若干个环,每次可以交换 \(x_0\) 和任意一个数的出边,最终使得每个点都是自环。
对于 \(x_0\) 初始所在的大小为 \(l_0\) 的环,可以通过 \(l_0 - 1\) 次操作处理。
对于其他非孤立点的大小为 \(l\) 的环,我们需要先把 \(x_0\) 合并上来,然后依次分离其他 \(l\) 个点,需要 \(l + 1\) 次操作。
最后得出两个排列各自操作数为奇数 / 偶数时的最小操作数,答案即为两个奇数的 max 与两个偶数的 max 的较小值。
注意奇数和偶数的搭配是不需要考虑的:对于枚举的每一种情况,可以发现其操作数奇偶性是唯一的。且某一种方案后面依次追加 \(1\sim n\) 后,就转变了另一种情况。
面对构造题,需要锻炼以下能力:
-
手摸
-
分类讨论
这通常需要草稿纸的帮助,所以不能只用脑子想,很容易出现细节错误(构造题有很多细节)。
手摸有利于发现通过题目给定的方法可以构造出什么操作体系(即这些操作可以干什么,比如这题就是交换两个数)。
分类讨论需要有条理,要具体。
对于求操作数最小值之类的问题,如果操作和操作体系过于复杂,一定需要转化,通常把一些动态的量转化为静态的量。
点击查看代码
#include <bits/stdc++.h>
#define ll int
#define ull unsigned ll
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
char buf[1 << 22], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
template <class T>
const inline void rd(T &x) {
char ch; bool neg = 0;
while(!isdigit(ch = getchar()))
if(ch == '-') neg = 1;
x = ch - '0';
while(isdigit(ch = getchar()))
x = (x << 1) + (x << 3) + ch - '0';
if(neg) x = -x;
}
const ll maxn = 2510, inf = 1e9, mod = 998244353;
ll power(ll a, ll b = mod - 2) {
ll s = 1;
while(b) {
if(b & 1) s = 1ll * s * a %mod;
a = 1ll * a * a %mod, b >>= 1;
} return s;
}
template <class T, class _T>
const inline ll pls(const T x, const _T y) { return x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline void add(T &x, const _T y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline void chkmax(T &x, const _T y) { x = x < y? y : x; }
template <class T, class _T>
const inline void chkmin(T &x, const _T y) { x = x < y? x : y; }
ll n, m, a[maxn], b[maxn], d[maxn], ca[maxn], cb[maxn];
vector <ll> resa[2], resb[2]; bool vis[maxn];
int main() {
rd(n), rd(m);
for(ll i = 1; i <= n; i++) rd(a[i]);
for(ll i = 1; i <= m; i++) rd(b[i]);
for(ll i = 1; i <= 1e4; i++)
for(ll j = 0; j < 2; j++)
resa[j].pb(0), resb[j].pb(0);
for(ll i = 0; i <= n; i++) {
for(ll j = 0; j <= n; j++) ca[a[j]] = j, vis[j] = false;
ll x; vector <ll> res; vis[i] = true;
for(x = ca[i]; x ^ i; x = ca[x])
res.pb((ca[x] - x + n + 1) % (n + 1)), vis[x] = true;
for(ll j = 0; j <= n; j++)
if(!vis[j] && ca[j] != j) {
res.pb((j - i + n + 1) % (n + 1)), vis[j] = true;
res.pb((ca[j] - j + n + 1) % (n + 1));
for(x = ca[j]; ca[x] ^ j; x = ca[x])
res.pb((ca[x] - x + n + 1) % (n + 1)), vis[x] = true;
res.pb((i - x + n + 1) % (n + 1)), vis[x] = true;
}
if(res.size() < resa[res.size() & 1].size())
resa[res.size() & 1] = res;
for(ll j = 0; j <= n; j++) a[j] = (a[j] + 1) % (n + 1);
}
for(ll i = 0; i <= m; i++) {
for(ll j = 0; j <= m; j++) cb[b[j]] = j, vis[j] = false;
ll x; vector <ll> res; vis[i] = true;
for(x = cb[i]; x ^ i; x = cb[x])
res.pb((cb[x] - x + m + 1) % (m + 1)), vis[x] = true;
for(ll j = 0; j <= m; j++)
if(!vis[j] && cb[j] != j) {
res.pb((j - i + m + 1) % (m + 1)), vis[j] = true;
res.pb((cb[j] - j + m + 1) % (m + 1));
for(x = cb[j]; cb[x] ^ j; x = cb[x])
res.pb((cb[x] - x + m + 1) % (m + 1)), vis[x] = true;
res.pb((i - x + m + 1) % (m + 1)), vis[x] = true;
}
if(res.size() < resb[res.size() & 1].size())
resb[res.size() & 1] = res;
for(ll j = 0; j <= m; j++) b[j] = (b[j] + 1) % (m + 1);
} vector <ll> A, B;
if(max(resa[0].size(), resb[0].size())
< max(resa[1].size(), resb[1].size())) A = resa[0], B = resb[0];
else A = resa[1], B = resb[1];
if(max(A.size(), B.size()) >= 9e3) return puts("-1"), 0;
while(A.size() < B.size()) A.pb(1), A.pb(n);
while(A.size() > B.size()) B.pb(1), B.pb(m);
printf("%d\n", (ll) A.size());
for(ll i = 0; i < A.size(); i++)
printf("%d %d\n", A[i], B[i]);
return 0;
}

浙公网安备 33010602011771号