BZOJ 3295 [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

Source

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

CDQ分治+树状数组~

抄阿当学长的代码抄出惯性……

据说这道题能用树套树做,orz

先记下所有询问,然后从后往前递推答案,也就是把删除变为加入,对于每一个加入位置,分别用两次CDQ分治计算出它左面和它右面的答案,这里用到树状数组记录,树状数组求逆序对详见:http://blog.csdn.net/senyelicone/article/details/52244090 ~


#include<cstdio>
#define ll long long

int n,m,x,c[100001],cnt,poi[100001],lans[100001],rans[100001];
ll ans[100001];

struct node{
	int x,id,tim;
}a[100001],b[100001];

int lowbit(int u)
{
	return u&(-u);
}

void add(int u,int v)
{
	for(int i=u;i<=n;i+=lowbit(i)) c[i]+=v;
}

int cal(int u)
{
	int totnum=0;
	for(int i=u;i;i-=lowbit(i)) totnum+=c[i];
	return totnum;
}

void findd(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[now].id<a[i].id;now++) add(a[now].x,1);
		lans[a[i].tim]+=now-l-cal(a[i].x);
	}
	for(int i=l;i<now;i++) add(a[i].x,-1);
	now=mid;
	for(int i=r;i>mid;i--)
	{
		for(;now>=l && a[now].id>a[i].id;now--) add(a[now].x,1);
		rans[a[i].tim]+=cal(a[i].x-1);
	}
	for(int i=now+1;i<=mid;i++) add(a[i].x,-1);
	findd(l,mid);findd(mid+1,r);
}

int main()
{
	scanf("%d%d",&n,&m);cnt=n;
	for(int i=1;i<=n;i++) scanf("%d",&a[i].x),a[i].id=i,poi[a[i].x]=i;
	for(int i=1;i<=m;i++) scanf("%d",&x),a[poi[x]].tim=cnt--;
	for(int i=1;i<=n;i++)
	  if(!a[i].tim) a[i].tim=cnt--;
	findd(1,n);
	for(int i=1;i<=n;i++) ans[i]=ans[i-1]+lans[i]+rans[i];
	for(int i=n;i>n-m;i--) printf("%lld\n",ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值