题意:
给出一个 n n n的排列,有 m m m次删除操作,每次删除其中一个元素 x x x,输出每次删除前序列的逆序对个数
cdq分治法:
与主席树相同的是,我们都是要删除一个位置时,这个位置所构成的逆序对个数,换句话说,就是求这个位置前面有多少大于 x x x的,后面有多少小于 x x x的。主席树能在线处理,动态修改权值线段树,使得某个区间的权值线段树任意查询,所以主席树可以在线处理这个问题。而换个角度思考,逆序对本身就是一种二维偏序关系,那在这里删除 x x x,能产生影响的一定是符合逆序对偏序关系的,即位置在前的比自己大的,位置在后比自己小的。但是仅仅按照这样子计算出删除某个位置的影响不能双向修改,比如 x x x和 y y y是一对逆序对,我们知道 x x x的影响是1,删去 x x x后 y y y的影响数也要减1。实际上删去 x x x能少去多少逆序对仅跟满足偏序条件和当前仍然存在的元素有关系,所以给元素多加一个维度的属性,删去时间
那么要求的就是满足如下偏序关系的对数了:
设 t [ i ] t[i] t[i]为 i i i元素删去的时间, v a l [ i ] val[i] val[i]是 i i i元素的权值, p o s [ i ] pos[i] pos[i]是 i i i元素的位置,在 t [ i ] < t [ j ] t[i]<t[j] t[i]<t[j]的条件下,符合
v a l [ j ] > v a l [ i ] , p o s [ j ] < p o s [ i ] val[j]>val[i],pos[j]<pos[i] val[j]>val[i],pos[j]<pos[i]
或
v a l [ j ] < v a l [ i ] , p o s [ j ] > p o s [ i ] val[j]<val[i],pos[j]>pos[i] val[j]<val[i],pos[j]>pos[i]
至此转换为一个三维偏序问题了,对两组偏序关系分别 c d q cdq cdq分治即可
一些细节:
存在一些元素不被删去,那么一个办法是令不被删去的元素的删去时间是无穷大即可
需要求出一开始的逆序对数,一个方法是在输入的时候二维偏序求解逆序对,另一个方法是令不被删去的元素的删去时间分别是 [ ∞ , ∞ + 1 , ∞ + 2 , . . . . . ] [\infty,\infty+1,\infty+2,.....] [∞,∞+1,∞+2,.....],在设置为无穷大时把他们的顺序设为依次,所有位置的答案加起来就是逆序对个数
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
struct fenwick
{
int tree1[200005],tree2[200005];
inline int lowbit(int x){return -x&x;}
void add(int x,int v)
{
int tmp=x*v;
while(x<=n)
{
tree1[x]+=v;
tree2[x]+=tmp;
x+=lowbit(x);
}
}
void add(int l,int r,int k)
{
add(l,k);
add(r+1,-k);
}
int getsum(int l,int r)
{
int ret=0,x=r;
while(x)
{
ret+=(r+1)*tree1[x]-tree2[x];
x-=lowbit(x);
}
x=l-1;
while(x)
{
ret-=l*tree1[x]-tree2[x];
x-=lowbit(x);
}
return ret;
}
}tree;
struct node
{
int val,t,pos;
}a[200005],tmp[200005];
int m,del[200005],pos[200005],ans[200005];
void solve1(int l,int r)
{
if(l==r) return;
int mid=l+r>>1;
solve1(l,mid); solve1(mid+1,r);
int j=l,i=mid+1,k=l;
while(j<=mid&&i<=r)
{
if(a[j].pos<=a[i].pos)
{
tree.add(a[j].val,a[j].val,1);
tmp[k++]=a[j++];
}
else
{
ans[a[i].pos]+=tree.getsum(a[i].val,n);
tmp[k++]=a[i++];
}
}
while(j<=mid)
{
tree.add(a[j].val,a[j].val,1);
tmp[k++]=a[j++];
}
while(i<=r)
{
ans[a[i].pos]+=tree.getsum(a[i].val,n);
tmp[k++]=a[i++];
}
for(int i=l;i<=mid;i++) tree.add(a[i].val,a[i].val,-1);
for(int i=l;i<=r;i++) a[i]=tmp[i];
}
void solve2(int l,int r)
{
if(l==r) return;
int mid=l+r>>1;
solve2(l,mid); solve2(mid+1,r);
int j=l,i=mid+1,k=l;
while(j<=mid&&i<=r)
{
if(a[j].pos>=a[i].pos)
{
tree.add(a[j].val,a[j].val,1);
tmp[k++]=a[j++];
}
else
{
ans[a[i].pos]+=tree.getsum(1,a[i].val);
tmp[k++]=a[i++];
}
}
while(j<=mid)
{
tree.add(a[j].val,a[j].val,1);
tmp[k++]=a[j++];
}
while(i<=r)
{
ans[a[i].pos]+=tree.getsum(1,a[i].val);
tmp[k++]=a[i++];
}
for(int i=l;i<=mid;i++) tree.add(a[i].val,a[i].val,-1);
for(int i=l;i<=r;i++) a[i]=tmp[i];
}
ll tot;
int q[200005];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].val);
a[i].pos=i;
pos[a[i].val]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d",&q[i]);
a[pos[q[i]]].t=i;
};
int pp=0x3fffffff;
for(int i=1;i<=n;i++)
if(!a[i].t) a[i].t=++pp;
sort(a+1,a+1+n,[&](const node& x,const node& y){
if(x.t!=y.t) return x.t>y.t;
if(x.pos!=y.pos) return x.pos<y.pos;
return x.val>y.val;
});
solve1(1,n);
sort(a+1,a+1+n,[&](const node& x,const node& y){
if(x.t!=y.t) return x.t>y.t;
if(x.pos!=y.pos) return x.pos>y.pos;
return x.val<y.val;
});
solve2(1,n);
for(int i=1;i<=n;i++) tot+=ans[i];
for(int i=1;i<=m;i++)
{
printf("%lld\n",tot);
tot-=ans[pos[q[i]]];
}
return 0;
}