n(n<=1e5)个数的数组a[],对应1-n的一个排列,
m(m<=5e4)次操作,每次先询问此时的逆序对数,再删去一个值
https://blog.csdn.net/sslz_fsy/article/details/86772265
计删去的ai所在位置pos,考虑逆序对的减少
减少的是pos前面大于ai的数量,和pos后面小于ai的数量
BIT套一下权值线段树就好,每个位置对应一棵1-n的权值线段树
防空间爆,所以实际是主席树动态开点
原来主席树只需要六行就能插链wtcl之前总结的什么破板子
今天彩排的时候闲着无聊,看了一下这个很久之前就听说过但是没学的东西
原来树套树的代码也是这么短,四五十行就搞出来了,之前想复杂了
其实就和之前学的链上建主席树差不多,就是按树状数组lowbit增序建主席树
然后每次更新一个点,都会带来log个点的链更新,每条链最多开log个点,
所以空间是O(n*logn*logn)的,时间也是O(n*logn*logn)的
BIT维护位置,主席树(权值线段树)维护 这个位置不断减lowbit可达的位置 所有位置的值的值域
敲完一发TLE两个点什么鬼,是我常数太大么,然而重在学方法,吸了口O2过了
#include
using namespace std;
typedef long long ll;
int read()
{
int cnt=0;char ch=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt;
}
const int N=1e5+10;
struct node{int l,r,num;}e[N*400];
//N*logn*logn,每次让logn个节点开长为logn的树链
int n,m,v;
ll ans;
int a[N],rt[N],id[N],tot;//id记录值的位置
void update(int &p,int l,int r,int pos,int v)
{
if(!p)p=++tot;
e[p].num+=v;
if(l==r)return;
int mid=(l+r)/2;
if(pos<=mid)update(e[p].l,l,mid,pos,v);
else update(e[p].r,mid+1,r,pos,v);
}
void add(int x,int pos,int v)//在BIT位置x上加一个v值
{
for(int i=x;i<=n;i+=i&-i)
update(rt[i],1,n,pos,v);
}
int ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return e[p].num;
int mid=(l+r)/2,res=0;
if(ql<=mid)res+=ask(e[p].l,l,mid,ql,qr);
if(qr>mid)res+=ask(e[p].r,mid+1,r,ql,qr);
return res;
}
ll sum(int x,int L,int R)//完全逆序序列 逆序对1e10/2
{
ll ans=0;
for(int i=x;i>0;i-=i&-i)
ans+=ask(rt[i],1,n,L,R);
return ans;
}
ll cal(int l,int r,int L,int R)//在BIT[l,r]区间内计算值域[L,R]间的数的个数
{
if(l>r||L>R)return 0;
return sum(r,L,R)-sum(l-1,L,R);
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;++i)
{
v=read();
ans+=cal(1,i-1,v+1,n);
add(i,v,1);
id[v]=i;
}
while(m--)
{
v=read();
printf("%lld\n",ans);
ans-=cal(1,id[v]-1,v+1,n);
ans-=cal(id[v]+1,n,1,v-1);
add(id[v],v,-1);
}
return 0;
}