BSOJ2841:CQOI2011动态逆序对 树状数组套平衡树

2845 -- 【CQOI2011】动态逆序对
Description
  对于序列A,它的逆序对数定义为满足i < j,且Ai > Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
Input
  输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下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
Hint
【样例解释】
  (1,5,3,4,2)  (1,3,4,2)  (3,4,2)  (3,2)  (3)。
【数据范围】
  编号   1-2    3-4    5-6    7-8    9-10
  n    <=1000  <=30000  <=50000  <=60000  <=100000

  m    <=100  <=10000  <=20000  <=40000  <=50000


由于删除有点难做,就把删除数存起来,倒着插入,再输出答案就行了。

树状数组存位置,平衡树存权值,输入的时候记录位置。

这次加了splay,其实区别不大。

#include<iostream>
#include<cstdio>
#include<cstring>
#define q 1000005
using namespace std;
struct Splaytree
{
	int l,r,data,fa,num,size;
	Splaytree()
	{
		l=r=fa=data=num=size=0;
	}
}tree[q];
int n,m;
int del[q]={0},a[q]={0},b[q]={0};
long long Ans[q]={0};
int pos[q]={0};
int temp=0,ct=0;
int root[q]={0};
int lowbit(int x)
{
	return x&(-x);
}
void Pushup(int x)
{
	tree[x].size=tree[tree[x].l].size+tree[tree[x].r].size+1;
}
inline void leftrotate(int x)
{
	int y=tree[x].fa;
	tree[y].r=tree[x].l;
	if(tree[x].l!=0)tree[tree[x].l].fa=y;
	tree[x].l=y;tree[x].fa=tree[y].fa;
	if(tree[y].fa!=0)
	{
		if(y==tree[tree[y].fa].l)tree[tree[y].fa].l=x;
		else tree[tree[y].fa].r=x;
	}
	tree[y].fa=x;
	Pushup(y);
}
inline void rightrotate(int x)
{
	int y=tree[x].fa;
	tree[y].l=tree[x].r;
	if(tree[x].r!=0)tree[tree[x].r].fa=y;
	tree[x].r=y;
	tree[x].fa=tree[y].fa;
	if(tree[y].fa!=0)
	{
		if(y==tree[tree[y].fa].l)tree[tree[y].fa].l=x;
		else tree[tree[y].fa].r=x;
	}
	tree[y].fa=x;
	Pushup(y);
}
inline void Splay(int x)
{
	while(tree[x].fa)
	{
	    int y=tree[x].fa,z=tree[y].fa;
		if(tree[y].fa==0)
		{
			if(tree[y].l==x)rightrotate(x);
			else leftrotate(x);
			break;
		}
		if(x==tree[y].l)
		{
			if(tree[tree[y].fa].l==y){rightrotate(y);rightrotate(x);}
			else {rightrotate(x);leftrotate(x);}
		}
		else
		{
			if(tree[tree[y].fa].l==y){leftrotate(x);rightrotate(x);}
			else {leftrotate(y);leftrotate(x);}
		}
	}
	Pushup(x);
}
void ins(int &root,int data,int fa)
{
	if(root==0){root=++ct;tree[root].fa=fa;tree[root].num=tree[root].size=1;tree[root].data=data;return;}
	tree[root].size++;
	if(data==tree[root].data){tree[root].num++;return;}
	if(data<tree[root].data)ins(tree[root].l,data,root);
	else ins(tree[root].r,data,root);
}
void Insert(int a,int data)
{
	int ctt=ct;
	for(int i=a;i<=n;i+=lowbit(i))
	{
		ins(root[i],data,0);
		if(ctt==ct)continue;
		Splay(ct);//如果有新建点,旋为根。
		root[i]=ct;
		ctt=ct;
	}
}
int Get(int root,int data)
{
	if(root==0)return 0;
	if(data>tree[root].data)return tree[tree[root].l].size+tree[root].num+Get(tree[root].r,data);
	else return Get(tree[root].l,data);
}
int get(int x,int data)
{
	if(x==0)return 0;
	int ans=0;
	for(int i=x;i;i-=lowbit(i))ans+=Get(root[i],data);
	return ans;
}
int getsize(int x)
{
	if(x==0)return 0;
	int ans=0;
	for(int i=x;i;i-=lowbit(i))ans+=tree[root[i]].size;
	return ans;
}
long long ans=0;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]),pos[a[i]]=i;
	for(int i=1;i<=m;i++)scanf("%d",&b[i]),del[b[i]]=1;
	
        //倒着做,先插入不删除的,再倒着插入删除的。
        for(int i=1;i<=n;i++)
	{
		if(!del[a[i]])
		{
			Insert(i,a[i]);
			ans+=get(n,a[i])-get(i,a[i]);//右边逆序对
			ans+=getsize(i-1)-get(i-1,a[i]);//左边
		}
	}
	for(int i=m;i>=1;i--)
	{
		Insert(pos[b[i]],b[i]);
		ans+=get(n,b[i])-get(pos[b[i]],b[i]);//右边
		ans+=getsize(pos[b[i]]-1)-get(pos[b[i]]-1,b[i]);//左边
		Ans[i]=ans;
	}
	for(int i=1;i<=m;i++)printf("%lld\n",Ans[i]);
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值