题意:动态逆序对
做法:对于删除操作可以当做逆向的添加操作 , 所以对于每个数字都有三个属性 (T,ID,Num) 所以这道题其实就是一个三维偏序问题 。 对于一个数 只需要 左边比它大的数 和右边比它小的数 ,我们可以用一个bit logn 进行操作。
三位偏序 可以用CDQ 减去一维 , 我们先把左边算出来 ,再把右边算出来,最后处理中间部分的。
对于中间的部分 用双指针 一个指向 L,MID(时间)。 一个指向MID+1,R(时间) 就可以消除T的影响 , 同时左右两边满足 id 是递增的!
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
struct node
{
int t,num,id;
}a[maxn],b[maxn];
int n, m;
int pos[maxn];
ll ans[maxn];
int l[maxn],r[maxn],bit[maxn];
inline void updata(int pos,int val)
{
for(int i=pos;i<=n;i+=(i&(-i)) ) bit[i]+=val;
}
inline int query(int pos)
{
int ans=0;
for(int i=pos;i;i-=(i&(-i))) ans+=bit[i];
return ans;
}
inline void cdq(int begin,int end)
{
if(begin >= end) return;
int mid=(begin+end)>>1;
int l1 = begin ,l2=mid+1,temp;
for(int i=begin;i<=end;++i)
{
if(a[i].t<=mid) b[l1++]=a[i];
else b[l2++]=a[i];
}
for(int i=begin;i<=end;++i) a[i]=b[i];
temp=begin;
for(int i=mid+1;i<=end;++i)
{
for( ; temp<=mid &&a[temp].id<a[i].id;temp++) updata(a[temp].num,1);
l[a[i].t]+=(temp - begin - query(a[i].num));
}
for(int i=begin;i<=temp-1;i++) updata(a[i].num,-1);
temp = mid;
for(int i=end;i>=mid+1;--i)
{
for(; temp>=begin && a[temp].id >a[i].id ; temp--) updata(a[temp].num,1);
r[a[i].t] += query(a[i].num-1);
}
for(int i=temp+1;i<=mid;i++) updata(a[i].num , -1);
cdq(begin,mid); cdq(mid+1,end);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
memset(bit,0,sizeof(bit));
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i].num);
a[i].id=i; a[i].t=0;
pos[a[i].num]=i;
}
int tt=n,x;
for(int i=1;i<=m;++i)
{
scanf("%d",&x);
a[pos[x]].t = tt--;
}
for(int i=1;i<=n;++i)
{
if(a[i].t==0) a[i].t=tt--;
}
cdq(1,n);
for(int i=1;i<=n;++i) ans[i]=ans[i-1] + l[i] + r[i];
for(int i=n;i>=n-m+1;--i) printf("%lld\n",ans[i]);
}
return 0;
}