luogu3157 [CQOI2011]动态逆序对

先算出一个点前头比它大和后头比它小的数量。
每次删点就扔进一个主席树里头,防止造成重复删答案。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int n, m, a[100005], p[100005], c[100005], uu, pre[100005], suf[100005];
int lson[5000005], rson[5000005], sum[5000005], orz, rot[100005];
long long ans;
int lowbit(int x){
	return x&-x;
}
int getSum(int x){
	int tmp=0;
	for(int i=x; i; i-=lowbit(i))	tmp += c[i];
	return tmp;
}
int query(int o, int l, int r, int x, int y){
	if(!o)	return 0;
	if(l>=x && r<=y)	return sum[o];
	else{
		int mid=(l+r)>>1;
		int ans=0;
		if(x<=mid)	ans += query(lson[o], l, mid, x, y);
		if(mid<y)	ans += query(rson[o], mid+1, r, x, y);
		return ans;
	}
}
int queryRange(int uu, int vv, int xx, int yy){//在uu到vv之间有多少数在xx和yy之间
	int tmp=0;
	for(int i=uu-1; i; i-=lowbit(i))
		tmp -= query(rot[i], 1, n, xx, yy);
	for(int i=vv; i; i-=lowbit(i))
		tmp += query(rot[i], 1, n, xx, yy);
	return tmp;
}
void update(int &rt, int l, int r, int x){
	if(!rt)	rt = ++orz;
	sum[rt]++;
	if(l==r)	return ;
	int mid=(l+r)>>1;
	if(x<=mid)	update(lson[rt], l, mid, x);
	if(mid<x)	update(rson[rt], mid+1, r, x);
}
int main(){
	cin>>n>>m;
	for(int i=1; i<=n; i++){
		scanf("%d", &a[i]);
		p[a[i]] = i;
		pre[i] = getSum(n) - getSum(a[i]);
		ans += pre[i];
		for(int j=a[i]; j<=n; j+=lowbit(j))	c[j]++;
	}
	memset(c, 0, sizeof(c));
	for(int i=n; i; i--){
		suf[i] = getSum(a[i]-1);
		for(int j=a[i]; j<=n; j+=lowbit(j))	c[j]++;
	}
	while(m--){
		printf("%lld
", ans);
		scanf("%d", &uu);
		uu = p[uu];
		ans -= pre[uu] + suf[uu];
		ans += queryRange(1, uu-1, a[uu]+1, n);
		ans += queryRange(uu+1, n, 1, a[uu]-1);
		for(int i=uu; i<=n; i+=lowbit(i))
			update(rot[i], 1, n, a[uu]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/poorpool/p/8336103.html