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
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;
}