题目:
题解:
记得很久以前给舒老师看这道题,他一眼秒了然后写了出来,给我造成了极大的心理创伤。。
cdq分治
首先可以考虑把这个处理过程倒过来,因为删数我们不擅长,但是添加是ok的
那么问题变成:一开始有一些数(全删完后剩的),求逆序对然后每添加一个数求一次逆序对
我们很快就可以发现这个题目的三个关键词:位置,时间,大小
三维偏序!
对于一个三元组(a,b,c),有两组满足“逆序对”的条件
a2 < a && b2 < b && c2 > c
a2 < a && b2 > b && c2 < c
那么我们要分开来加,而且因为添加的先后性并不会加重
那么我们对于时间排序,位置分治,这都很套路,加值的时候维护对于每个值在ta要消失时刻产生的逆序对贡献,那么我们输出的时候就要对于时间累加输出
树套树(挖坑
代码:
cdq分治
#include <cstdio>
#include <iostream>
#include <algorithm>
#define LL long long
using namespace std;
const int N=100005;
struct hh{int z,wz,tim;LL ans;}f[N],jl[N];
int n,m,a[N],in[N];LL c[N];bool ok[N];
void add(int loc,int val)
{
for (int i=loc;i<=n;i+=i&(-i))
c[i]+=val;
}
LL qurry(int loc)
{
LL ans=0;
for (int i=loc;i>=1;i-=i&(-i)) ans+=c[i];
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 t1=l,t2=mid+1,all=0;
for (int i=l;i<=r;i++)
if ((t1<=mid && f[t1].wz<=f[t2].wz)||t2>r) jl[i]=f[t1++];
else jl[i]=f[t2++];
for (int i=l;i<=r;i++)
if ((f[i]=jl[i]).tim<=mid) add(f[i].z,1),all++;
else f[i].ans+=all-qurry(f[i].z);
for (int i=l;i<=r;i++) if (f[i].tim<=mid) add(f[i].z,-1);
for (int i=r;i>=l;i--)
if (f[i].tim<=mid) add(f[i].z,1);
else f[i].ans+=qurry(f[i].z);
for (int i=l;i<=r;i++) if (f[i].tim<=mid) add(f[i].z,-1);
}
int cmp(hh a,hh b){return a.tim<b.tim;}
int main()
{
scanf("%d%d",&n,&m);
int x,tem=0;
for(int i=1;i<=n;i++)
scanf("%d",&x),a[x]=i;
for(int i=1;i<=m;i++)
scanf("%d",&in[i]),ok[in[i]]=1;
for(int i=1;i<=n;i++)
if(!ok[i]) f[++tem].z=i,f[tem].wz=a[f[tem].z],f[tem].tim=tem;
for(int i=m;i;i--)
f[++tem].z=in[i],f[tem].wz=a[f[tem].z],f[tem].tim=tem;
cdq(1,n);
sort(f+1,f+n+1,cmp);
for (int i=2;i<=n;i++)
f[i].ans+=f[i-1].ans;
for (int i=n;i>n-m;i--) printf("%lld\n",f[i].ans);
}