QOJ #4424. Babushka and her pierogi 题解

Description

给定 n,Cn,C 和两个排列 a1,a2,,ana_1,a_2,\ldots,a_nb1,b2,,bnb_1,b_2,\ldots,b_n,每次可以用 aiaj+C|a_i-a_j|+C 的代价交换 aia_iaja_j。问将序列从 aa 变成 bb 的最小代价。

n106n\leq 10^6

Solution

先连 aibia_i\to b_i,每次相当于是交换两个点的出边。

交换次数的下界是 nn 减置换环数量,aiaj|a_i-a_j| 这个权值的下界是 aibi\sum|a_i-b_i|,容易发现这两个都能取到下界。

考虑怎么证明这件事情。

首先每次交换的两个位置一定满足 bjaiajbib_j\leq a_i\leq a_j\leq b_i,且在同一置换环中,否则一定会浪费。

prebi=ai,nxtai=bipre_{b_i}=a_i,nxt_{a_i}=b_i,考虑找到当前最小的数 xx,每次操作 xx,直到 nxtx=xnxt_x=x。我们相当于是要找到一个 yy,使得 xyprexnxtyx\leq y\leq pre_x\leq nxt_y

不妨设 xx 所在的置换环中有 cc 个在 [x,prex1][x,pre_x-1] 之间的数,那么这 cc 个数有 cc 个出边,而在 [x,prex1][x,pre_x-1] 的入边最多只有 c1c-1 个,因为 prexpre_x 不在这个范围内。所以一定能找到这样的 yy 进行操作。

暴力跳 nxtnxtyy 的复杂度是 O(n2)O(n^2),过不了,考虑优化。

这里的技巧是从 xx 同时跳 preprenxtnxtyy,则找到 yy 的次数为 O(环长/2)O(环长/2),进一步会发现次数为当前的环分裂后的较小的环长。根据启发式分裂的思想,这个东西的总复杂度就是 O(nlogn)O(n\log n)

时间复杂度:O(nlogn)O(n\log n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 2e5 + 5;

int n, c, cost;
int a[kMaxN], b[kMaxN], unq[kMaxN], pos[kMaxN], pre[kMaxN], nxt[kMaxN];
std::vector<std::pair<int, int>> vec;

void work(int x, int y) {
int i = pos[x], j = pos[y];
cost += abs(unq[a[i]] - unq[a[j]]) + c;
int a1 = x, b1 = nxt[x], a2 = y, b2 = nxt[y];
std::swap(a[i], a[j]);
std::swap(pos[x], pos[y]);
nxt[a1] = b2, pre[b2] = a1;
nxt[a2] = b1, pre[b1] = a2;
vec.emplace_back(i, j);
}

void discrete() {
std::sort(unq + 1, unq + 1 + n);
for (int i = 1; i <= n; ++i) {
a[i] = std::lower_bound(unq + 1, unq + 1 + n, a[i]) - unq;
b[i] = std::lower_bound(unq + 1, unq + 1 + n, b[i]) - unq;
nxt[a[i]] = b[i], pre[b[i]] = a[i];
pos[a[i]] = i;
}
}

void dickdreamer() {
std::cin >> n >> c;
cost = 0, vec.clear();
for (int i = 1; i <= n; ++i) {
std::cin >> a[i] >> b[i];
unq[i] = a[i];
}
discrete();
for (int i = 1; i <= n; ++i) {
for (; pre[i] != i;) {
// std::cerr << i << ' ' << pre[i] << '\n';
for (int x = i, y = i;; x = pre[x], y = nxt[y]) {
if (x <= pre[i] && nxt[x] >= pre[i]) {
work(pre[i], x);
break;
}
if (y <= pre[i] && nxt[y] >= pre[i]) {
work(pre[i], y);
break;
}
}
}
}
std::cout << cost << ' ' << vec.size() << '\n';
for (auto [x, y] : vec) std::cout << x << ' ' << y << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

QOJ #4424. Babushka and her pierogi 题解
https://sobaliuziao.github.io/2025/08/28/post/ea602b50.html
作者
Egg_laying_master
发布于
2025年8月28日
许可协议