BZOJ传送门
Luogu传送门
以前一直想用树套树过掉此题,太懒不想写。。。(我不会告诉你我其实是不会树套树做法因为实在太恶心了。。。)
然后呢,现在掌握了新技能以后发现这题就可以很快地过去了
我们可以把删除操作看成倒着加入操作,然后。。。
这题就变成了时间为t时的逆序对数目,我们可以看成一个三维偏序,即a[i].t<=a[j].t,a[i].x < a[j].x,a[i].v > a[j].v
(t表示加入时间,x表示位置,v表示值)
所以就可以用CDQ大法了!!!
首先按照t把小于等于mid的移到左边去,右边同理
然后计算左边对右边的影响
即对于处于右边的第i个位置,计算逆序对数目
这个逆序对可以这么统计,先找位置比它小但值比它大的,再找位置比它大但值比它小的
可以调用两次树状数组实现
还是一样,最后清空树状数组,统计答案时累加即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct ppap{int x,v,t;}a[100001],b[100001];
int p[100001],n,m;
ll ans[100001],f[100001],q[100001],h[100001];
inline int lowbit(int x){return x&-x;}
inline void add(int x,int v){for(;x<=n;x+=lowbit(x))f[x]+=v;}
inline int sum(int x){int ans=0;for(;x;x-=lowbit(x))ans+=f[x];return ans;}
void cdq(int l,int r){
if(l==r)return;
int mid=l+r>>1,b1=l-1,b2=mid;
for(int i=l;i<=r;i++)if(a[i].t<=mid)b[++b1]=a[i];
else b[++b2]=a[i];
for(int i=l;i<=r;i++)a[i]=b[i];
int cc=l;
for(int i=mid+1;i<=r;i++){
for(;cc<=mid&&a[cc].x<a[i].x;cc++)add(a[cc].v,1);
q[a[i].t]+=(ll)cc-l-sum(a[i].v);
}
for(int i=l;i<cc;i++)add(a[i].v,-1);
cc=mid;
for(int i=r;i>mid;i--){
for(;cc>=l&&a[cc].x>a[i].x;cc--)add(a[cc].v,1);
h[a[i].t]+=(ll)sum(a[i].v);
}
for(int i=mid;i>cc;i--)add(a[i].v,-1);
cdq(l,mid);cdq(mid+1,r);
}
int main()
{
scanf("%d%d",&n,&m);int x;
for(int i=1;i<=n;i++)a[i].x=i,scanf("%d",&a[i].v),p[a[i].v]=i;
for(int i=n;i>n-m;i--)scanf("%d",&x),a[p[x]].t=i;
int k=n-m;
for(int i=1;i<=n;i++)if(!a[i].t)a[i].t=k--;
cdq(1,n);
for(int i=1;i<=n;i++)ans[i]=ans[i-1]+q[i]+h[i];
for(int i=n;i>n-m;i--)printf("%lld\n",ans[i]);
return 0;
}