【BZOJ3295】【Cqoi2011】动态逆序对

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1


样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)
(3)。

HINT

N<=100000 M<=50000

题解

cdq分治经典题。

先记下所有询问,然后从后往前递推答案,也就是把删除变为加入,对于每一个加入位置,分别用两次CDQ分治计算出它左面和它右面的答案,再用树状数组记录逆序对。

为什么这样做就可以呢?由于有点得删除,所以对于同一个点在不同时间的逆序对数不同。也就是说时间影响逆序对数,按照每个点的删除顺序把点从n到1标号,在cdq分治的时候枚举一个中间时间mid,把数列分成两部分,因为两部分的编号是递增的,时间是打乱的,所以可以这样做,这样的话左边的数一定比右边的数晚删,就算左边对右边的贡献,分两部分:左边的数在右边的前面并比右边的大,左边的数在右边的后面的比右边的小,,就这样分治下去就行了,最后就可以求出每个点在它所在的时间内产生的逆序对数了。

#include
#include
#include
#include
#define ll long long
using namespace std;
const int maxn=100010;
int n,m,t[maxn];
ll lans[maxn],rans[maxn],ans[maxn];
struct node{
	int id,x,tim;
}a[maxn],b[maxn];
int pos[maxn];
void add(int x,int y){
	for(int i=x;i<=n;i+=i&(-i))
	t[i]+=y;
}
int query(int x){
	int ans=0;
	for(int i=x;i>=1;i-=i&(-i))
	ans+=t[i];
	return ans;
}
void cdq(int l,int r){
	if(l>=r) return ;
	int mid=(l+r)>>1,l1=l,l2=mid+1,now=l;
	for(int i=l;i<=r;i++){
		if(a[i].tim<=mid) b[l1++]=a[i];
		else b[l2++]=a[i];
	}
	for(int i=l;i<=r;i++) a[i]=b[i];//只算左边对右边的影响,因为右边先被删。 
	for(int i=mid+1;i<=r;i++){
		for(;now<=mid&&a[i].id>a[now].id;now++) add(a[now].x,1);
		lans[a[i].tim]+=now-l-query(a[i].x);//不用+1因为for之后now又加了1; 
	}
	for(int i=l;imid;i--){
		for(;now>=l&&a[i].idn-m;i--) printf("%lld\n",ans[i]); 
	return 0; 
}

你可能感兴趣的:(cdq分治)