0%

ARC181D Prefix Bubble Sort

传送门

Solution

先用树状数组求逆序对,得出数组总共的逆序对数量 tottot。设 aia_i 和它前面的数共组成 inviinv_i 对逆序对。

手玩样例后不难理解,操作 ii 的本质,其实就是对所有 jij\le i,若 aja_j 非零,则 aj1a_j-1。设执行操作 ii 前有 mmaja_j 非零,则执行操作后整个序列的逆序对数量减少 mm

由于操作保证升序,所以每次操作都可以把新的非零 aja_j 放入优先队列,并将优先队列里的元素全部减一。当然,只用新建一个变量 flgflg O(1)O(1) 记录减的值即可。若计算得出队头为零,则弹出。mm 即为优先队列的规模。totmtot-m 为当前答案。

每个 aia_i 最多入队出队各一次,时间复杂度为 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
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll N = 1e6 + 10;

ll n, a[N], q, sum[N], tr[N], ans, flg;
priority_queue<ll, vector<ll>, greater<ll> > que;

ll lowbit(ll x) {return x & -x;}

void add(ll x, ll c)
{
for (; x <= n; x += lowbit(x))
tr[x] += c;
}

ll query(ll x)
{
ll res = 0;
for (; x > 0; x -= lowbit(x))
res += tr[x];
return res;
}

int main()
{
scanf("%lld", &n);
for (ll i = 1; i <= n; i++)
scanf("%lld", &a[i]);
scanf("%lld", &q);
ll p = 1;
for (ll i = 1; i <= n; i++) {
sum[i] = i - query(a[i]) - 1;
ans += sum[i];
add(a[i], 1);
}
while (q--) {
++flg;
ll x;
scanf("%lld", &x);
while (p <= x) {
if (sum[p] + flg - 1 > 0 && sum[p])
que.push(sum[p] + flg - 1);
++p;
}
ans -= que.size();
while (que.size() && que.top() <= flg)
que.pop();

printf("%lld\n", ans);
}
return 0;
}