BZOJ3295 动态逆序对(树状数组套线段树)

时间:2024-09-21 15:06:32

[Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 6058  Solved: 2117
[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

Source

题解:

   普通的树状数组,无法记录位置,不能知道一次之后记录的是所以位置的信息,

   一次删除的时候,不能知道哪些值是比它小的,因为位置的影响,

   所以需要树套树,在开一层位置,这样就可以了,动态开点,n logn log n的空间复杂度。

 #include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath> #define N 100007
#define M 10000007
using namespace std;
int n,m,cnt;
int a[N],id[N],bit[N],lson[M],rson[M],tree[M];
long long ans; int lowbit(int x){return x&-x;}
void addy(int &u,int l,int r,int x,int y)
{
if(!u)u=++cnt;
if(l==r){tree[u]+=y;return;}
int mid=(l+r)>>;
if(x<=mid) addy(lson[u],l,mid,x,y);
else addy(rson[u],mid+,r,x,y);
tree[u]=tree[lson[u]]+tree[rson[u]];
}
void addx(int x,int y,int val)
{
for (int i=x;i<=n;i+=lowbit(i))
addy(i,,n,y,val);
}
int queryy(int u,int l,int r,int i,int j)
{
if(!u) return ;
if(l==i&&r==j) return tree[u];
int mid=(l+r)>>;
if(j<=mid) return queryy(lson[u],l,mid,i,j);
else if(i>mid) return queryy(rson[u],mid+,r,i,j);
return queryy(lson[u],l,mid,i,mid)+queryy(rson[u],mid+,r,mid+,j);
}
int queryx(int x,int l,int r)
{
int ans=;
for (int i=x;i>=;i-=lowbit(i))
ans+=queryy(i,,n,l,r);
return ans;
}
int main()
{
scanf("%d%d",&n,&m),cnt=n;
for(int i=;i<=n;i++)
{
scanf("%d",&a[i]);
ans+=(long long)queryx(i-,a[i]+,n);
id[a[i]]=i;
addx(i,a[i],);
}
while(m--)
{
printf("%lld\n",ans);
int x;scanf("%d",&x);
ans-=(long long)queryx(id[x]-,x+,n);
ans-=(long long)queryx(n,,x-);
ans+=(long long)queryx(id[x]-,,x-);
addx(id[x],x,-);
}
}