校内赛 T4 算法入门

题目描述

1
2
for (int i = 1; i < n; ++i)
if (a[i] > a[i + 1]) swap(a[i], a[i + 1]);

现在有一个长度为 $n$ 的数列,有 $m$ 此操作,每次操作交换 $a[x]$ 和 $a[x+1]$ 或者输出第 $k$ 执行以上冒泡排序代码过程中成功 $swap$ 的次数

$n,m\le 10^6$,其中数列中的元素互不相同

Solution

首先比较直接的是,整个数列一共需要交换逆序对次就能变的有序

我们考虑如何计算还剩多少次交换才能使数列有序,即计算逆序对的数量

我们定义 $f_i=\sum_{j=1}^i [a_j>a_i]$,整个数列的逆序对数位 $\sum_{i=1}^n f_i$

我们考虑每次进行冒泡排序对 $f$ 的影响是什么

首先对于不会发生移动的数,它的 $f$ 值一定为 $0$,所以我们不需要考虑

对于发生移动的数,他一定是向前移动一位,且它前面一个比它大的数跑到了他的后面

所以 $f_i=f_{i+1}-1$

在交换 $x$ 轮之后,$f$ 整个左移 $x$ 位,且都减少了 $x$

所以对于原 $f$,在交换 $x$ 之后我们需要统计的是 $\sum f_i[fi>x]-x\sum [f_i >x]$

由于左移消失的前 $x$ 个 $f$ 的值一定小于 $x$,所以我们不需要考虑左移

上面那个东西可以直接拿两个树状数组维护

时间复杂度 $O(n\log n)$

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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <queue>
#define gc getchar
#define maxn 1000010
#define cQ const Queue
#define INF 1000000000
#define ll long long
#define lowbit(i) ((i) & (-i))
using namespace std;

int read() {
int x = 0; char c = gc();
while (!isdigit(c)) c = gc();
while (isdigit(c)) x = x * 10 + c - '0', c = gc();
return x;
}

int n, m, a[maxn];

int s[maxn];

int b[maxn], cnt;
void init_hash() {
for (int i = 1; i <= n; ++i) b[i] = a[i];
sort(b + 1, b + n + 1); cnt = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + cnt + 1, a[i]) - b;
}

struct Bit {
int Bit[maxn], n;

inline void add(int i, int v) { i++; while (i <= n) Bit[i] += v, i += lowbit(i); }

inline ll get_sum(int i) {
ll s = 0; i++;
while (i) s += Bit[i], i -= lowbit(i);
return s;
}

void clear() {
for (int i = 1; i <= n; ++i) Bit[i] = 0;
}
} B1, B2;

ll sum;
inline void solve_1() {
int x = read();
if (a[x] > a[x + 1]) {
B1.add(s[x + 1], -s[x + 1]); B2.add(s[x + 1], -1);
--s[x + 1]; --sum;
B1.add(s[x + 1], s[x + 1]); B2.add(s[x + 1], 1);
}
else {
B1.add(s[x], -s[x]); B2.add(s[x], -1);
++s[x]; ++sum;
B1.add(s[x], s[x]); B2.add(s[x], 1);
}
swap(s[x], s[x + 1]); swap(a[x], a[x + 1]);
}

inline ll calc(int x) {
int z = B1.get_sum(x), y = B2.get_sum(x);
return sum - B1.get_sum(x) - x * (n - B2.get_sum(x));
}

inline void solve_2() {
int x = read(), y = calc(x - 1);
printf("%d\n", calc(x - 1) - calc(x));
}

int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) a[i] = read();
init_hash(); B1.n = cnt + 1;
for (int i = 1; i <= n; ++i) {
s[i] = i - 1 - B1.get_sum(a[i] - 1);
B1.add(a[i], 1); sum += s[i];
} B1.clear(); B1.n = B2.n = n + 1;
for (int i = 1; i <= n; ++i) B1.add(s[i], s[i]), B2.add(s[i], 1);
for (int i = 1; i <= m; ++i) {
int opt = read();
if (opt == 1) solve_1();
else solve_2();
}
return 0;
}