题解:SP861 SWAPS - Counting inversions
主要使用分块和树状数组,通过从右向左遍历原数组并使用树状数组计算初始逆序对数目;在每次更新操作时,通过调整树状数组和块内值动态更新逆序对数目。
分块:将原数组分成若干块,每块大小大约为 \(\sqrt{n}\),便于局部处理。
与分块相关的函数如下:
-
update(int i, int idx, int val):- 更新第
i块的树状数组中位置idx的值。
- 更新第
-
sum(int i, int idx):- 查询第
i块的树状数组中,小于等于idx的元素数量。
- 查询第
-
get(int l, int r, int x):- 查询区间
[l, r]内小于等于x的元素个数。
- 查询区间
更多细节请看代码注释:
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0;
char c = getchar();
while(c < '0' || c > '9') {
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 3) + (x << 1) + c - '0';
c = getchar();
}
return x;
}
const int N = 2.5e5 + 5; // 序列最大长度
const int M = 5e4 + 5; // 元素最大值
const int SQN = 1005; // 块大小的近似平方根
int n; // 序列长度
int arr[N]; // 序列
int q; // 操作次数
int x, y; // 每次操作的 x 和 y
int start[SQN]; // 每块的起始位置
int finish[SQN]; // 每块的结束位置
int block[N]; // 记录每个元素属于哪个块
int bit[SQN][M] = {0}; // 每块一个树状数组
void update(int i , int idx , int val) {
while(idx < M) {
bit[i][idx] += val;
idx += idx & -idx;
}
}
int sum(int i , int idx) {//用于计算当前块中小于等于指定值的元素数量
int res = 0;
while(idx) {
res += bit[i][idx];
idx -= idx & -idx;
}
return res;
}
long long inv = 0;
int get(int l , int r , int x) {//查询区间内小于等于 x 的元素个数
int res = 0;
for(int i = block[l] ; i <= block[r] ; ++i) {
if(start[i] >= l && finish[i] <= r) {
res += sum(i , x);
} else {
for(int j = max(start[i] , l) ; j <= min(finish[i] , r) ; ++j) {
res += (arr[j] <= x);
}
}
}
return res;
}
int main() {
n = read();
for(int i = 1 ; i <= n ; ++i) {
arr[i] = read();
}
int i = 1;
int cur = 0;
while (i <= n) {
int j = i;
start[++cur] = i; // 当前块的起始位置
while (j <= n && j < i + SQN) { // 当前块的结束位置
block[j] = cur; // 记录每个元素属于哪个块
update(cur, arr[j], 1); // 更新当前块的树状数组
++j;
}
finish[cur] = j - 1; // 记录当前块的结束位置
i = j;a
}
for (int i = n; i >= 1; --i) {
inv += sum(0, arr[i] - 1); // 查询当前元素左侧比它小的元素数量
update(0, arr[i], 1); // 将当前元素加入到树状数组中
}
q = read();
while(q--) {
x = read() , y = read();
inv -= x - 1 - get(1 , x - 1 , arr[x]);
inv -= get(x + 1 , n , arr[x] - 1);//减去旧值对逆序对数目的影响
update(block[x] , y , 1);
update(block[x] , arr[x] , -1);
arr[x] = y;//更新树状数组和块内值
inv += x - 1 - get(1 , x - 1 , arr[x]);
inv += get(x + 1 , n , arr[x] - 1);//加上新值对逆序对数目的影响
printf("%lld\n" , inv);
}
return 0;
}

浙公网安备 33010602011771号