P3157 [CQOI2011]动态逆序对

时间:2023-03-10 07:52:02
P3157 [CQOI2011]动态逆序对

P3157 [CQOI2011]动态逆序对

https://www.luogu.org/problemnew/show/P3157

题目描述

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

输入输出格式

输入格式:

输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。

输出格式:

输出包含m行,依次为删除每个元素之前,逆序对的个数。

输入输出样例

输入样例#1:
5 4
1
5
3
4
2
5
1
4
2
输出样例#1:
5
2
2
1 样例解释
(1,5,3,4,2) (1,3,4,2) (3,4,2) (3,2) (3)。

说明

N<=100000 M<=50000

树状数组套主席树

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
#define N 100005
using namespace std; struct sair{
int l,r,sum;
}tree[N*];
int n;
int root[N],a[N],b[N],c[N],cnt;
void Add(int x){
while(x<=n){
c[x]++;
x+= x&(-x);
}
} int getsum(int x){
int sum=;
while(x){
sum+=c[x];
x-= x&(-x);
}
return sum;
} void add(int cur,int l,int r,int p,int v){
tree[cur].sum+=v;
if(l==r){
return;
}
int mid=(l+r)/;
if(p<=mid){
if(!tree[cur].l){
tree[cur].l=++cnt;
}
add(tree[cur].l,l,mid,p,v);
}
else{
if(!tree[cur].r){
tree[cur].r=++cnt;
}
add(tree[cur].r,mid+,r,p,v);
}
} int query(int L,int R,int cur,int l,int r){
if(!cur){
return ;
}
if(L<=l&&R>=r){
return tree[cur].sum;
}
int mid=(l+r)/;
int ans=;
if(L<=mid) ans+=query(L,R,tree[cur].l,l,mid);
if(R>mid) ans+=query(L,R,tree[cur].r,mid+,r);
return ans;
} int main(){
int m,v,p,j;
scanf("%d %d",&n,&m);
long long ans=;
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
b[a[i]]=i;
Add(a[i]);
ans+=getsum(n)-getsum(a[i]);
for(j=i;j<=n;j+= j&(-j)){
if(!root[j]){
root[j]=++cnt;
}
add(root[j],,n,a[i],);
}
}
for(int i=;i<=m;i++){
printf("%lld\n",ans);
scanf("%d",&v);
p=b[v];
j=p-;
while(j){
ans-=query(v+,n,root[j],,n);
j-= j&(-j);
}
j=n;
while(j){
ans-=query(,v-,root[j],,n);
j-= j&(-j);
}
j=p;
while(j){
ans+=query(,v-,root[j],,n);
j-= j&(-j);
}
j=p;
while(j<=n){
add(root[j],,n,v,-);
j+= j&(-j);
}
}
}