题目描述
对于序列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
分析
将删除看做倒着插入,记录三元组{time,pos,val}
time表示第几个插入,很明显第一个删除的time为n
pos表示在原队列中的位置
val表示值
我们按time排序,发现对当前time的答案有贡献的为
time_i<time
pos_i>pos && val_i<val
或者 pos_i<pos && val_i>val
于是就是3维偏序问题了
#include<bits/stdc++.h>
#define N 200005
#define LL long long
using namespace std;
struct Node{int t,x,y;}q[N],tmp[N];
int n,m,Time,a[N],match[N],c[N];
LL ans[N];
bool cmp(Node a,Node b){return a.t<b.t;}
int read(){
int cnt=0;char ch=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt;
}
void add(int x,int val){for(;x<=n;x+=x&-x) c[x]+=val;}
int quary(int x){
int ans=0;
for(;x;x-=x&-x) ans+=c[x];
return ans;
}
void CDQ(int l,int r){
if(l==r) return;
int mid=(l+r)>>1;
CDQ(l,mid),CDQ(mid+1,r);
int i=l,j=mid+1;
//处理pos_i<pos 且 val_i>val的
for(int k=l;k<=r;k++){
if(j>r||(i<=mid&&q[i].x<q[j].x)) add(q[i].y,1),tmp[k]=q[i++];
else ans[q[j].t]+=quary(n)-quary(q[j].y),tmp[k]=q[j++];
}
for(int k=l;k<=mid;k++) add(q[k].y,-1);
for(int k=l;k<=r;k++) q[k]=tmp[k];
//处理pos_i>pos && val_i<val 的贡献 (已经按x排好序)
for(int i=r;i>=l;i--){
if(q[i].t<=mid) add(q[i].y,1);
else ans[q[i].t]+=quary(q[i].y);
}
for(int i=l;i<=r;i++)
if(q[i].t<=mid) add(q[i].y,-1);
}
int main(){
n=read(),m=read(); Time=n;
for(int i=1;i<=n;i++){
a[i]=read();
q[i].t=0 , q[i].x=i , q[i].y=a[i];
match[q[i].y]=i;
}
for(int i=1;i<=m;i++){
q[match[read()]].t=Time--;
}
for(int i=1;i<=n;i++)
if(q[i].t==0) q[i].t=Time--;
sort(q+1,q+n+1,cmp);
CDQ(1,n);
for(int i=1;i<=n;i++) ans[i]+=ans[i-1];
for(int i=n;i>=n-m+1;i--) printf("%lld\n",ans[i]);
return 0;
}