bzoj3295[Cqoi2011] 动态逆序对

题目链接:bzoj3295

题目大意:

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


题解:

cdq分治

当前的逆序对数=总逆序对数-含删除元素的逆序对数+删除元素序列中新增加的逆序对数(因为一个逆序对会被重复删

而总逆序对数和含删除元素的逆序对数可以在一开始处理出来。所以目标就是要求每个删除操作后删除元素序列中新增加的逆序对数,这个用cdq来做。

额删除序列就是被删除的元素的序列,按原序列的顺序来排序。譬如样例删了5 1 4 2这四个元素,那么删除序列就是1 5 4 2

要用LL!!!

其实↑都是对popoqqq的理解orzorzorz

呐第一道cdq分治,所以说cdq分治是划分操作序列?每次处理出前半部分操作对后半部分操作的影响?吧做多点啊。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 110000
typedef long long LL;

struct node
{
	LL x,y,pos;
}q[maxn],tol[maxn],tor[maxn];
bool cmp(node x,node y)
{
	return x.y<y.y;
}LL a[maxn];
LL n,m,tot,ans,w[maxn],c[maxn];
LL tim[maxn],cnt[maxn],f[maxn];
//tot-当前的时间 tim[x]-c[x]的时间位置
//额这个意会(?)就是一个顺序(时间戳?而已
//cnt[i]-含有位置i这个元素的逆序对数
//ans-当前答案 f[i]-第i个删除操作后新删除序列增加的逆序对数
LL lowbit(LL x){return x&(-x);}
//向上修改
void change1(LL x){
	for (x;x<=n;x+=lowbit(x)) {
		if (tim[x]!=tot) c[x]=0,tim[x]=tot;
		c[x]++;
	}
}
//向下修改
void change2(LL x){
	for (x;x>0;x-=lowbit(x)) {
		if (tim[x]!=tot) c[x]=0,tim[x]=tot;
		c[x]++;
	}
}
//向上查询
LL query1(LL x)
{
	LL ret=0;
	for (x;x<=n;x+=lowbit(x)) if (tim[x]==tot) ret+=c[x];
	return ret;
}
//向下查询
LL query2(LL x)
{
	LL ret=0;
	for (x;x>0;x-=lowbit(x)) if (tim[x]==tot) ret+=c[x];
	return ret;
}
void solve(LL l,LL r)//cdq分治
{
	LL mid=(l+r)>>1;
	if (l==r) 
	{
		printf("%lld\n",ans);
		ans-=cnt[q[mid].y];
		ans+=f[mid];return;
	}
	//分,处理前半部分(因为后半部分对前半部分没有影响
	LL i,lnum=0,rnum=0;
	for (i=l;i<=r;i++)
	 if (q[i].pos<=mid) tol[++lnum]=q[i];
	 else tor[++rnum]=q[i];
	for (i=0;i<lnum;i++) q[l+i]=tol[i+1];
	for (i=0;i<rnum;i++) q[l+i+lnum]=tor[i+1];
	solve(l,mid);
	//处理前半部分对后半部分的影响 
	//就是搞 处理完前半部分的删除后 f[]的改变 
	tot++;LL j=l;
	for (i=mid+1;i<=r;i++)
	{
		for (;j<=mid && q[j].y<q[i].y;j++)
		  change2(q[j].x);
		f[q[i].pos]+=query1(q[i].x);
	}
	tot++;j=mid;
	for (i=r;i>mid;i--)
	{
		for (;j>=l && q[j].y>q[i].y;j--)
			change1(q[j].x);
		f[q[i].pos]+=query2(q[i].x);
	}
	solve(mid+1,r);//搞后半部分
	lnum=l;rnum=mid+1;//归并
	for (i=l;i<=r;i++)
	{
		if ( (q[lnum].y<q[rnum].y || rnum>r) && lnum<=mid)
			tol[i]=q[lnum++];
		else tol[i]=q[rnum++];
	}
	for (i=l;i<=r;i++) q[i]=tol[i];
}
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	memset(tim,0,sizeof(tim));
	LL i;
	scanf("%lld%lld",&n,&m);tot=0;
	for (i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		w[a[i]]=i;
		cnt[i]=query1(a[i]);
		change2(a[i]);
		ans+=cnt[i];
	}
	tot++;
	for (i=n;i>=1;i--)
	{
		cnt[i]+=query2(a[i]);
		change1(a[i]);
	}
	for (i=1;i<=m;i++)
	{
		scanf("%lld",&q[i].x);
		q[i].y=w[q[i].x];
		q[i].pos=i;f[i]=0;
	}
	sort(q+1,q+1+m,cmp);
	solve(1,m);
	return 0;
}


划分序列?每次处理出前半部分对后半部分的影响
posted @ 2017-01-17 11:31  OxQ  阅读(107)  评论(0编辑  收藏  举报