[Cqoi2011]动态逆序对

题意

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

分析

参照broxin的题解。

这本质上是一个三维偏序,分别是时间,下标,数值,记为(t,x,y)。

我们可以把删除的过程倒过来,当做插入来做,时间t表示这个数是第几个插入的,显然给出的删除的点的t值依次是N,N-1,N-2...(越先删除的视为越后插入的)注意不在询问范围内的点的t值可以任意设置,并且显然没有哪两个点有相同的t或x或y值,这使得问题好考虑得多了。我们求的就是按顺序插入每一个数时,这个数左边比它大的、右边比它小的分别有多少个。形式化地,对一个点(t0,x0,y0),求出满足t<t0,x<x0,y>y0的点的个数记为lda[t0],满足t<t0,x>x0,y>y0的点的个数记为rxiao[t0]。

我想了一会儿,觉得最外层按x排比较科学,内部对t进行划分排序(相当于快排,将t值<=mid的划分到左边,同时对于划分到同一侧的点要保证原来的相对顺序不变),对y用树状数组来维护。每个节点[L,R]划分出来是这样的:

要找[L,mid]对[mid+1,R]的贡献:

先考虑对lda的贡献。枚举t∈[mid+1,R]的点(t0,x0,y0),区间内的点由于已经按时间划分好了,所以不需要考虑t<t0这一条件。只需找出左区间中x<x0且y>y0的点,由于两边的x值各自保持单调(如图),所以可以像树状数组求逆序对一样,将[L,mid]区间内的点的y值在树状数组上增加1,然后求[mid+1,R]的每个y值在树状数组上的前缀和即可。由于l1和l2都是单增的,这一操作复杂度为nlogn。

再考虑对rxiao的贡献:类似地,找出[L,mid]中x值大于[mid+1,R]中的x值的即可。

注意一层分治并不能找出[mid+1,R]中所有值的lda和rxiao,但整个分治一定会不遗漏不重复地覆盖每个点的决策区间。每层复杂度nlogn,总共logn层,总复杂度nlog^2n。貌似有人说cdq分治可以做到nlogn?我觉得不太科学,毕竟三维,不可能把某一维直接吃掉吧。。

cdq分治做这种题真是优秀,空间复杂度仅为O(n),时间复杂度也不逊于高级数据结构,并且分治(对半分)以及树状数组的常数都是极小的,基本是严格logn。

快排式的cdq,学习了。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
#define lowbit(x) (x&-x)
template<class T>il T read(){
	rg T data=0,w=1;
	rg char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(isdigit(ch))
		data=data*10+ch-'0',ch=getchar();
	return data*w;
}
template<class T>il T read(rg T&x){
	return x=read<T>();
}
typedef long long ll;

co int N=1e5+1;
int n,m;
namespace T{
	int c[N];
	void add(int p,int v){
		for(int i=p;i<=n;i+=lowbit(i))
			c[i]+=v;
	}
	int sum(int p){
		int re=0;
		for(int i=p;i;i-=lowbit(i))
			re+=c[i];
		return re;
	}
}

struct dot{
	int t,x,y; // every t,x,y is unique
}a[N],np[N];
int lda[N],rxiao[N];
ll ans[N];
void cdq(int L,int R){
	using namespace T;
	if(L>=R) return;
	int mid=(L+R)/2;
	
	int l1=L,l2=mid+1;
	for(int i=L;i<=R;++i){
		if(a[i].t<=mid) np[l1++]=a[i];
		else np[l2++]=a[i];
	}
	std::copy(np+L,np+R+1,a+L);
	
	l1=L;
	for(int i=mid+1;i<=R;++i) // smaller x, bigger y
	{
		for(;l1<=mid&&np[l1].x<np[i].x;++l1)
			add(np[l1].y,1);
		lda[np[i].t]+=(l1-L)-sum(np[i].y);
	}
	for(int i=L;i<l1;++i)
		add(np[i].y,-1);
	
	l1=mid;
	for(int i=R;i>mid;--i){
		for(;l1>=L&&np[l1].x>np[i].x;--l1)
			add(np[l1].y,1);
		rxiao[np[i].t]+=sum(np[i].y-1);
	}
	for(int i=l1+1;i<=mid;++i)
		add(np[i].y,-1);
	
	cdq(L,mid),cdq(mid+1,R);
}

int pos[N];
int main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	read(n),read(m);
	for(int i=1;i<=n;++i)
		read(a[i].y),a[i].x=i,pos[a[i].y]=i;
	int t,tmr=n;
	for(int i=1;i<=m;++i)
		read(t),a[pos[t]].t=tmr--;
	for(int i=1;i<=n;++i) if(!a[i].t)
		a[i].t=tmr--;
	cdq(1,n);
	for(int i=1;i<=n;++i)
		ans[i]=ans[i-1]+rxiao[i]+lda[i];
	for(int i=n;i>n-m;--i)
		printf("%lld\n",ans[i]);
	return 0;
}

posted on 2019-02-14 07:51  autoint  阅读(132)  评论(0编辑  收藏  举报

导航