题意
传送门 UVa 11990
顺序删除数字可以用一维 BIT 查询逆序数,这里是随机位置删除数字,需要增加数字在数列中所处位置的 1 个维度, ( i , d a t [ i ] ) (i, dat[i]) (i,dat[i]) 代表索引为 i i i, 值为 d a t [ i ] dat[i] dat[i] 的数列上某一个数字。位于该数字对应的二维坐标点左上和右下的位置的点,都可与其构成逆序数对。
问题变成了如何在二维空间里高效查询某一矩形范围的点数,二维 BIT 实现简单,但空间会爆。这里采用分桶法 + 平方分割,考虑到每一次计算需对桶做求和,每一个桶维护当前列从第 0 行至当前行所代表矩形范围的点数,求和操作 O ( n 1 / 2 ) O(n^{1/2}) O(n1/2) 在列方向求和即可。每次增加一个点,就计算当前空间内能与其配成逆序数对的点数,更新总逆序数。
实现上逆着删除次序操作,即每次仅考虑增加点即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define INF 0x3f3f3f3f
#define delta 0.85
#define eps 1e-3
#define PI 3.14159265358979323846
#define MAX_N 200005
#define MAX_M 100005
using namespace std;
typedef long long LL;
const int B_SIZE = 450;
int N, M;
int dat[MAX_N], id[MAX_N]; //数据与索引的映射
int qry[MAX_M];
bool mask[MAX_N], used[MAX_N]; //代表是否为需删除节点,是否已添加
int bucket[B_SIZE][B_SIZE];
LL res[MAX_M];
void add(int x, int y){
int bx = x / B_SIZE, by = y / B_SIZE;
for(int i = bx; i <= N / B_SIZE; i++) ++bucket[i][by];
}
// 求 [0, x] * [0, y] 区域总点数
int sum(int x, int y){
int bx = (x + 1) / B_SIZE, by = (y + 1) / B_SIZE;
int sum = 0;
if(bx > 0){
for(int i = 0; i < by; i++) sum += bucket[bx - 1][i];
}
for(int i = bx * B_SIZE; i <= x; i++){
if(used[i] && dat[i] <= y) ++sum;
}
for(int i = by * B_SIZE; i <= y; i++){
if(used[id[i]] && id[i] < bx * B_SIZE) ++sum;
}
return sum;
}
//求左上与右下区域点数
int cal(int x, int y){
return sum(x, N) + sum(N, y) - 2 * sum(x, y);
}
int main(){
while(~scanf("%d%d", &N, &M)){
memset(bucket, 0, sizeof(bucket));
memset(mask, 0, sizeof(mask));
memset(used, 0, sizeof(used));
for(int i = 1; i <= N; i++){
scanf("%d", dat + i);
id[dat[i]] = i;
}
for(int i = 0; i < M; i++){
int n;
scanf("%d", &n);
mask[qry[i] = id[n]] = 1;
}
LL inv = 0;
for(int i = 1; i <= N; i++){
if(!mask[i]){
inv += cal(i, dat[i]);
add(i, dat[i]);
used[i] = 1;
}
}
for(int i = M - 1; i >= 0; i--){
int x = qry[i], y = dat[x];
inv += cal(x, y);
res[i] = inv;
add(x, y);
used[x] = 1;
}
for(int i = 0; i < M; i++) printf("%lld\n", res[i]);
}
return 0;
}