CF1637H Minimize Inversions Number 题解

Description

给定一个 1n1\sim n 的排列 pp。 你可以进行下列操作正好一次:

  • 选定 pp 的一个长度为 kk 的子序列,并将其按照相同的顺序移动到 pp 的最前面。

对于 k=0,1,,nk=0,1,\ldots,n,分别求出 pp 在操作后的最小逆序对数。

1n5×1051\leq n\leq 5\times 10^5

Solution

考虑已经选定了 q1,q2,,qkq_1,q_2,\ldots,q_k 表示操作的数,怎么表示逆序对的减少量

首先如果 k=1k=1,则减少量为 j=1i1[pj>pi]j=1i1[pj<pi]\displaystyle\sum_{j=1}^{i-1}{[p_j>p_i]}-\sum_{j=1}^{i-1}{[p_j<p_i]},设其为 did_i

则对于 k>1k>1 的情况可以得到如下式子:

减少量=i=1k(j=1qi1[pj>pqi]j=1qi1[pj<pqi]j=1i1[pqj>pqi]+j=1i1[pqj<pqi])=(k2)+i=1kdqi2i=1kj=i+1k[pqi>pqj]\begin{aligned} 减少量=&\sum_{i=1}^{k}\left(\sum_{j=1}^{q_i-1}{[p_j>p_{q_i}]}-\sum_{j=1}^{q_i-1}{[p_j<p_{q_i}]}-\sum_{j=1}^{i-1}{[p_{q_j}>p_{q_i}]}+\sum_{j=1}^{i-1}{[p_{q_j}<p_{q_i}]}\right)\\ =&\binom{k}{2}+\sum_{i=1}^{k}d_{q_i}-2\sum_{i=1}^{k}\sum_{j=i+1}^{k}{[p_{q_i}>p_{q_j}]} \end{aligned}

所以现在只需要最大化 i=1kdqi2i=1kj=i+1k[pqi>pqj]\displaystyle\sum_{i=1}^{k}d_{q_i}-2\sum_{i=1}^{k}\sum_{j=i+1}^{k}{[p_{q_i}>p_{q_j}]} 了。

经过手玩会有一种感觉是如果存在 i<ji<jpi>pjp_i>p_j 的话 jj[1,i1][1,i-1][j+1,n][j+1,n] 的贡献都比 ii 要优,而 [i+1,j1][i+1,j-1] 这部分二者是差不多的,所以可以猜测如果存在 jj 满足 i<j,pi>pji<j,p_i>p_jii 选了 jj 没选就一定不优。

证明就考虑每次找到这样的 (i,j)(i,j)jij-i 最小的一对,可以发现 [i+1,j1][i+1,j-1] 中不能有取值在 [pj+1,pi1][p_j+1,p_i-1] 内的,否则不管选不选都能和 ii 或者 jj 构成更小的对。同样的,取值在 [pi+1,n][p_i+1,n] 都不能选,[1,pj1][1,p_j-1] 都选了。

[i+1,j1][i+1,j-1] 取值在 [1,pj1][1,p_j-1] 的有 cntcnt 个,那么式子里第二部分的增量至少为 2(cnt)=2cnt-2\cdot(-cnt)=2\cdot cnt,同时 djd_jdid_i 少算了至多 [i+1,j1][i+1,j-1] 之间的 cntcnt 个数,所以将 ii 换成 jj 的变化量至少为 2cntcnt=cnt02\cdot cnt-cnt=cnt\geq 0,这说明我们每次选择这样的 (i,j)(i,j) 调整一定不劣。

最后一定能调整成猜测的情况。

那么此时选的数 ii 的贡献即为 ci=di2j=i+1n[pi>pj]\displaystyle c_i=d_i-2\sum_{j=i+1}^{n}{[p_i>p_j]}

预处理出这个数组然后每次选择最大的 kk 个选即可。

时间复杂度: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
#include <bits/stdc++.h>

// #define int int64_t

using i64 = int64_t;

const int kMaxN = 5e5 + 5;

int n;
int a[kMaxN], b[kMaxN];

struct BIT {
int c[kMaxN];
void clear() { std::fill_n(c + 1, n, 0); }
void upd(int x, int v) {
for (; x <= n; x += x & -x) c[x] += v;
}
int qry(int x) {
int ret = 0;
for (; x; x -= x & -x) ret += c[x];
return ret;
}
} bit;

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i];
i64 ans = 0;
bit.clear();
for (int i = 1; i <= n; ++i) {
int cnt = i - 1 - bit.qry(a[i]);
ans += cnt, b[i] = cnt - (i - 1 - cnt);
bit.upd(a[i], 1);
}
bit.clear();
for (int i = n; i; --i) {
b[i] -= 2 * bit.qry(a[i]);
bit.upd(a[i], 1);
}
std::sort(b + 1, b + 1 + n, std::greater<>());
std::cout << ans << ' ';
for (int i = 1; i <= n; ++i) {
ans -= b[i];
std::cout << ans - 1ll * i * (i - 1) / 2 << ' ';
}
std::cout << '\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;
}

CF1637H Minimize Inversions Number 题解
https://sobaliuziao.github.io/2025/03/25/post/8e1c8dc2.html
作者
Egg_laying_master
发布于
2025年3月25日
许可协议