Loading

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;
}
posted @ 2025-03-31 11:16  Sktn0089  阅读(13)  评论(0)    收藏  举报