【bzoj3295】 CQOI2011动态逆序对 树状数组+主席树

对于每一个位置我们处理出刚开始它左边比他大的数的个数,和它右边比它小的数的个数,然后每次删除就在主席树里修改,最后答案就要减去a1+a2-左边比他大的被删除了的数的个数-右边比他小的被删除了的数的个数。

一开始傻逼了,还对整个序列建前缀主席树,还以为省空间,结果发现树状数组直接预处理就好了,最后在不断地RE过程中,艰难的A了,看来卡空间的题真是非常不爽呢。


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 10000010

using namespace std;

int lch[maxn],rch[maxn],cnt[maxn];
int s[100010],root[100010],a[100010];
int l[30],r[30],rank[100010];
int n,T,tot,llen,rlen,a1[100010],a2[100010];
long long ans;

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

long long query(int i)
{
	long long ans=0;
	while (i)
	{
		ans+=s[i];
		i-=lowbit(i);
	}
	return ans;
}

void add(int i,int x)
{
	while (i<=n)
	{
		s[i]+=x;
		i+=lowbit(i);
	}
}

int modify(int pre,int l,int r,int x,int f)
{
	int now=++tot;
	if (l==r)
	{
		cnt[now]=cnt[pre]+f;lch[now]=rch[now]=0;
	}
	else
	{
		int mid=(l+r)/2;
		if (x<=mid)
		{
			rch[now]=rch[pre];lch[now]=modify(lch[pre],l,mid,x,f);
		}
		else
		{
			lch[now]=lch[pre];rch[now]=modify(rch[pre],mid+1,r,x,f);
		}
		cnt[now]=cnt[lch[now]]+cnt[rch[now]];
	}
	return now;
}

void modify(int x,int t,int f)
{
	for (int i=x;i<=n;i+=lowbit(i)) root[i]=modify(root[i],1,n,t,f);
}

long long query(int L,int R,int x,int y)
{
	if (x>y) return 0;
	if (L==x && R==y)
	{
		long long sum=0;
		for (int i=1;i<=llen;i++) sum-=cnt[l[i]];
		for (int i=1;i<=rlen;i++) sum+=cnt[r[i]];
		return sum;
	}
	int mid=(L+R)/2;
	if (y<=mid)
	{
		for (int i=1;i<=llen;i++) l[i]=lch[l[i]];
		for (int i=1;i<=rlen;i++) r[i]=lch[r[i]];
		return query(L,mid,x,y);
	}
	if (mid<x)
	{
		for (int i=1;i<=llen;i++) l[i]=rch[l[i]];
		for (int i=1;i<=rlen;i++) r[i]=rch[r[i]];
		return query(mid+1,R,x,y);
	}
	long long sum=0;
	int a[30],b[30];
	for (int i=1;i<=llen;i++) a[i]=l[i];
	for (int i=1;i<=rlen;i++) b[i]=r[i];
	for (int i=1;i<=llen;i++) l[i]=lch[a[i]];
	for (int i=1;i<=rlen;i++) r[i]=lch[b[i]];
	sum+=query(L,mid,x,mid);
	for (int i=1;i<=llen;i++) l[i]=rch[a[i]];
	for (int i=1;i<=rlen;i++) r[i]=rch[b[i]];
	sum+=query(mid+1,R,mid+1,y);
	return sum;
}

int main()
{
	scanf("%d%d",&n,&T);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int i=n;i>=1;i--)
	{
		ans+=query(a[i]);a2[i]=query(a[i]);
		add(a[i],1);
	}
	memset(s,0,sizeof(s));
	for (int i=1;i<=n;i++)
	{
		a1[i]=query(n)-query(a[i]);
		add(a[i],1);
	}
	for (int i=1;i<=n;i++) rank[a[i]]=i;
	tot=0;root[0]=lch[0]=rch[0]=cnt[0]=0;
	for (int i=1;i<=n;i++) root[i]=root[0];
	while (T--)
	{
		printf("%lld\n",ans);
		int x,pos;
		scanf("%d",&x);
		pos=rank[x];
		
		llen=0;rlen=0;
		for (int i=pos-1;i;i-=lowbit(i)) r[++rlen]=root[i];
		ans+=query(1,n,x+1,n);
		
		llen=0;rlen=0;
		for (int i=pos;i;i-=lowbit(i)) l[++llen]=root[i];
		for (int i=n;i;i-=lowbit(i)) r[++rlen]=root[i];
		ans+=query(1,n,1,x-1);
		ans-=a1[pos]+a2[pos];
		
		modify(pos,x,1);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值