12
27
2015
0

【bzoj3295】【Cqoi2011】动态逆序对

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2280  Solved: 725
[Submit][Status][Discuss]

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

树状数组包线段树。。。

我们令删除的数为x,位置在loc。

首先容易想到的就是找到1~(loc-1)大于x的个数,和(loc+1)~n小于x的个数。这就是裸的主席树上加树状数组。但是这样内存是Nlog2N,是会炸的。于是我们换一种维护方式,我们先预处理出每个位置的front和back,front是指位置在其之前且值大于其值的个数,back是指位置在其之后且值小于其值的个数。

那么我们需要在主席树上维护就是删去的数。这样的内存是Mlog2N的,就可以解决这道题了。

代码:

 

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int n,m,x,loc,cnt;
int L[35],R[35],front[100010],back[100010],b[100010],root[100010],pos[100010],a[100010];
ll ans;
struct node{int l,r;ll sum;}tr[8000010];
int ask(int x){int ans=0;for (;x;x-=x&-x) ans+=b[x];return ans;}
void add(int x){for (;x<=n;x+=x&-x) b[x]++;}
ll ask2(int l,int r,int x)
{
	if (l>r) return 0;
	l--;
	L[0]=R[0]=0;
	for (int i=l;i;i-=i&-i)
		L[++L[0]]=root[i];
	for (int i=r;i;i-=i&-i)
		R[++R[0]]=root[i];
	l=1;r=n;
	ll sum=0;
	while (l<r)
	{
		int mid=(l+r)>>1;
		if (x<=mid)
		{
			for (int i=1;i<=R[0];i++) sum+=tr[tr[R[i]].r].sum;
			for (int i=1;i<=L[0];i++) sum-=tr[tr[L[i]].r].sum;
			for (int i=1;i<=R[0];i++) R[i]=tr[R[i]].l;
			for (int i=1;i<=L[0];i++) L[i]=tr[L[i]].l;
			r=mid;
		}else
		{
			for (int i=1;i<=L[0];i++) L[i]=tr[L[i]].r;
			for (int i=1;i<=R[0];i++) R[i]=tr[R[i]].r;
			l=mid+1;
		}
	}
	return sum;
}
ll ask1(int l,int r,int x)
{
	if (l>r) return 0;
	l--;
	L[0]=R[0]=0;
	for (int i=l;i;i-=i&-i)
		L[++L[0]]=root[i];
	for (int i=r;i;i-=i&-i)
		R[++R[0]]=root[i];
	l=1;r=n;
	ll sum=0;
	while (l<r)
	{
		int mid=(l+r)>>1;
		if (x>mid)
		{
			for (int i=1;i<=R[0];i++) sum+=tr[tr[R[i]].l].sum;
			for (int i=1;i<=L[0];i++) sum-=tr[tr[L[i]].l].sum;
			for (int i=1;i<=R[0];i++) R[i]=tr[R[i]].r;
			for (int i=1;i<=L[0];i++) L[i]=tr[L[i]].r;
			l=mid+1;
		}else
		{
			for (int i=1;i<=L[0];i++) L[i]=tr[L[i]].l;
			for (int i=1;i<=R[0];i++) R[i]=tr[R[i]].l;
			r=mid;
		}
	}
	return sum;
}
void change(int &p,int l,int r,int x)
{
	tr[++cnt]=tr[p];p=cnt;tr[p].sum++;
	if (l==r) return;
	int mid=(l+r)>>1;
	if (x<=mid) change(tr[p].l,l,mid,x);else change(tr[p].r,mid+1,r,x);
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		pos[a[i]]=i;
		front[i]=i-1-ask(a[i]);
		ans+=front[i];
		add(a[i]);
	}
	memset(b,0,sizeof(b));
	for (int i=n;i>=1;i--)
	{
		back[i]=ask(a[i]);
		add(a[i]);
	}
	while (m--)
	{
		printf("%lld\n",ans);
		scanf("%d",&x);
		loc=pos[x];
		ans-=front[loc]-ask2(1,loc-1,x);
		ans-=back[loc]-ask1(loc+1,n,x);
		for (;loc<=n;loc+=loc&-loc) change(root[loc],1,n,x);
	}
}
Category: 主席树 | Tags: | Read Count: 329

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com