题目大意
给出1-n的一个排列和m个操作,每个操作有一个数字x表示在序列中把x这个数删掉,并且求在删掉x前该序列中有多少个逆序对。
n<=100000,m<=50000
分析
这题看完题后就想到了可以用树套树来搞,但是比较麻烦(其实也不是很麻烦)并且对于n<=100000,m<=50000的数据范围如果用nlog^2的复杂度去做的话会跑的很慢,于是就选择了新学的cdq分治。
先一开始往序列里面插入没被删除的数,然后从后往前的顺序处理操作。把每个操作拆分成两个操作,一个是插入,一个是询问,顺序不限。
那么问题就转换成了对于三元组(x,y,z)的处理,x是操作序号,y是下标,z是具体数值。对于这种问题一般的处理方式都是第一维直接排序,第二维cdq分治掉,第三维树状数组。
具体的就跟bzoj 3262一样啦。
注意要开long long
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;
int c[N],a[N],pos[N],n,m,cnt,tot,vis[N],num[N],ans[N];
struct data{int x,pos,op,id;}q[N*10],t[N*10];
void ins(int x,int y)
{
while (x<=n)
{
c[x]+=y;
x+=x&(-x);
}
}
int query(int x)
{
int ans=0;
while (x)
{
ans+=c[x];
x-=x&(-x);
}
return ans;
}
void cdq(int l,int r)
{
if (l>=r) return;
int mid=(l+r)/2;
cdq(l,mid);cdq(mid+1,r);
for (int j=mid+1,i=l;j<=r;j++)
{
while (q[i].pos<q[j].pos&&i<=mid)
{
if (q[i].op==1) ins(q[i].x,1);
i++;
}
if (q[j].op==2) ans[q[j].id]+=query(q[j].x-1);
}
for (int i=l,j=mid+1;j<=r;j++)
while (q[i].pos<q[j].pos&&i<=mid)
{
if (q[i].op==1) ins(q[i].x,-1);
i++;
}
for (int i=mid,j=r;j>mid;j--)
{
while (q[i].pos>q[j].pos&&i>=l)
{
if (q[i].op==1) ins(q[i].x,1);
i--;
}
if (q[j].op==2) ans[q[j].id]+=query(n)-query(q[j].x);
}
for (int i=mid,j=r;j>mid;j--)
while (q[i].pos>q[j].pos&&i>=l)
{
if (q[i].op==1) ins(q[i].x,-1);
i--;
}
int i=l,j=mid+1,k=l;
while (i<=mid||j<=r)
if (q[i].pos<q[j].pos&&i<=mid||j>r) t[k++]=q[i++];
else t[k++]=q[j++];
for (int i=l;i<=r;i++)
q[i]=t[i];
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for (int i=1;i<=m;i++)
{
scanf("%d",&num[i]);
vis[pos[num[i]]]=1;
}
ll sum=0;
for (int i=1;i<=n;i++)
{
sum+=query(n)-query(a[i]);
ins(a[i],1);
}
for (int i=1;i<=n;i++)
ins(a[i],-1);
for (int i=1;i<=n;i++)
if (!vis[i])
{
q[++tot].op=1;
q[tot].pos=i;
q[tot].x=n-a[i]+1;
}
for (int i=m;i>=1;i--)
{
q[++tot].op=1;
q[tot].pos=pos[num[i]];
q[tot].x=n-num[i]+1;
q[++tot].op=2;
q[tot].pos=pos[num[i]];
q[tot].x=n-num[i]+1;
q[tot].id=++cnt;
}
cdq(1,tot);
for (int i=cnt;i>=1;i--)
{
printf("%lld\n",sum);
sum-=ans[i];
}
return 0;
}