BZOJ3295: [Cqoi2011]动态逆序对(带修主席树)

3295: [Cqoi2011]动态逆序对
**Time Limit: 10 Sec Memory Limit: 128 MB
Description
对于序列A,它的逆序对数定义为满足 i < j i<j i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删
除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数
Input
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。
以下n行每行包含一个1到n之间的正整数,即初始排列。
以下m行每行一个正整数,依次为每次删除的元素。
N<=100000 M<=50000
Output
输出包含m行,依次为删除每个元素之前,逆序对的个数。
Sample Input
5 4
1
5
3
4
2
5
1
4
2
Sample Output
5
2
2
1
样例解释
KaTeX parse error: Unexpected character: '' at position 12: (1,5,3,4,2)̲(1,3,4,2)(3,4,…

这道题显然是一道带修改主席树(或者cdq分治),主要讲讲如何用带修改主席树来解决这个问题。

首先各位大佬要先保证自己会用树状数组求逆序对,这样我们可以求出最开始的答案值(当然你用线段树,cdq分治,归并排序写也可以,只是码量相对较大)。想用知道cdq分治求逆序对的原理的请戳这里。

那么我们思考一下,如果我们知道上一次的答案,我们只需要计算出这一次修改的差量即可,怎么计算差量呢?我们知道,如果第 i i i个数被删除了,那么逆序对总数会减少在 i i i之前没被删除的且比 a i a_i ai大的数的总数加上在 i i i之后没被删除的且比 a i a_i ai小的数的总数。不难看出这两个量是一种对称的关系,这样的话我们只用思考其中一种情况。

S o So So,如何维护在 i i i之前没被删除的且比 a i a_i ai大的数的总数呢?做了那么多题,我们应当想到正难则反。我们可以先统计出原数列中在 i i i之前且比 a i a_i ai大的数,在统计出被删除的数中在 i i i之前且比 a i a_i ai大的数,前者可以在统计逆序对时顺便求出,后者直接用带修改主席树求一求就行了。

至此,我们已经解决了这个问题。最后吐槽一句:BZOJ时空限制真的有点烦人。

代码如下:

#include
#define N 100005
using namespace std;
inline int read(){
	int ans=0,w;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
	return ans;
}
long long ans=0,bit1[N],ans1[N],ans2[N];
int a[N],n,m,mp[N],rt[N*50],siz=0;
struct Node{int l,r,sum;}T[N*50];
inline int lowbit(int x){return x&-x;}
inline void add1(int x,int v){for(int i=x;i<=n;i+=lowbit(i))bit1[i]+=v;}
inline long long query1(int x){long long ret=0;for(int i=x;i;i-=lowbit(i))ret+=bit1[i];return ret;}
inline void add(int &p,int l,int r,int v){
	if(!p)p=++siz;
	++T[p].sum;
	if(l==r)return;
	int mid=l+r>>1;
	if(v<=mid)add(T[p].l,l,mid,v);
	else add(T[p].r,mid+1,r,v);
}
inline long long query(int p,int l,int r,int ql,int qr){
	if(qr<l||ql>r)return 0;
	if(ql<=l&&r<=qr)return T[p].sum;
	int mid=l+r>>1;
	return query(T[p].l,l,mid,ql,qr)+query(T[p].r,mid+1,r,ql,qr);
	
}
inline long long ask(int l,int r,int ql,int qr){
	if(ql>qr)return 0;
	long long ret=0;
	for(;l;l-=lowbit(l))ret-=query(rt[l],1,n,ql,qr);
	for(;r;r-=lowbit(r))ret+=query(rt[r],1,n,ql,qr);
	return ret;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		mp[a[i]]=i;
		ans1[i]=query1(n)-query1(a[i]);
		ans+=ans1[i];
		add1(a[i],1);
	}
	for(int i=1;i<=n;++i)bit1[i]=0;
	for(int i=n;i>=1;--i){
		ans2[i]=query1(a[i]-1);
		add1(a[i],1);
	}
	for(int i=1;i<=m;++i){
		printf("%lld\n",ans);
		int now,pos;
		scanf("%lld",&now);
		pos=mp[now];
		ans-=(ans1[pos]+ans2[pos])-ask(0,pos,now+1,n)-ask(pos,n,1,now-1);
		for(int j=pos;j<=n;j+=lowbit(j))
			add(rt[j],1,n,now);
	}
	return 0;
}

你可能感兴趣的:(#,主席树,#,树状数组)