题意:动态计算逆序对,给出n,m,n是数组大小,m是删的数的个数,要求每次删完数后剩下的逆序对个数。
思路:若普通主席树的话,每次删一个数,则要更新后面n个数的大小,复杂度nlogn,直接爆炸,所以考虑一种可以单点修改和区间查询的数据结构,线段树或树状数组都可以,不过显然树状数组好写,所以用树状数组来代替主席树的root数组,求出总的逆序对后,每次删除一个数m就减去从1到m-1求大于m的数的个数,然后再减去从m+1到n的小于m的数的个数。
没用快读,不过开o2可过
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-x))
using namespace std;
const int N = 1e5 + 10;
int n, m, root[N], cnt, pos[N];
ll ans;
struct node {
int l, r, num;
} zxs[N * 300];
void add(int l, int r, int &now, int pos, int k) {
if(!now)
now = ++cnt;
zxs[now].num += k;
if(l == r)
return;
int m = (l + r) >> 1;
if(pos <= m)
add(l, m, zxs[now].l, pos, k);
else
add(m + 1, r, zxs[now].r, pos, k);
}
void ins(int i, int x, int v) {
for (int j = i; j <= n; j += lowbit(j))
add(1, n, root[j], x, v);
}
int ask(int p, int l, int r, int ql, int qr) {
if(ql <= l && r <= qr)
return zxs[p].num;
int m = (l + r) / 2, res = 0;
if(ql <= m)
res += ask(zxs[p].l, l, m, ql, qr);
if(qr > m)
res += ask(zxs[p].r, m + 1, r, ql, qr);
return res;
}
ll sum(int x, int L, int R) {
ll ans = 0;
for(int i = x; i > 0; i -= lowbit(i))
ans += ask(root[i], 1, n, L, R);
return ans;
}
ll calc(int l, int r, int L, int R) {
if(l > r || L > R)
return 0;
return sum(r, L, R) - sum(l - 1, L, R);
}
int main() {
scanf("%d%d", &n, &m);
for(int v, i = 1; i <= n; i++) {
scanf("%d", &v), pos[v] = i;
ans += calc(1, i - 1, v + 1, n);
ins(i, v, 1);
}
for(int x, i = 1; i <= m; i++) {
scanf("%d", &x);
printf("%lld\n", ans);
ans -= (calc(1, pos[x] - 1, x + 1, n) + calc(pos[x] + 1, n, 1, x - 1));
ins(pos[x], x, -1);
}
return 0;
}