思路
做法是树状数组套主席树。
考虑每次删除一个元素对答案的影响。删除位于第个位置的元素(指数字大小)后,答案减少的数量是位置小于且比大的数字的个数(记为以及位置大于且比小的数字的个数(记为)。那么每次删除答案都要减少。
但是这样并没有考虑到之前已经删除过的元素,因此我们还要在此基础上把多减去的那些再加回来(也就是某个元素在之前已经被删除了,但是在删除当前元素的时候仍然计算了它俩的逆序对)。
也就是说我们要在所有删除的元素中找到位置在且比大的元素的个数,以及位置在之间且比小的元素的个数。如果这些元素一直不变,显然可以通过建一棵主席树来做。但是在这道题中每次删除都会向这个序列中插入一个值,因此每次都要重新建一颗主席树,复杂度太高。
主席树维护的其实就是前缀和(或者说基于前缀的某些信息),这道题又是单点修改和区间查询。所以可以用树状数组的思想来优化,树状数组每个节点存一颗权值线段树(动态开点),线段树维护区间和,这样每次对删除序列进行修改就变成,总的复杂度优化为。
线段树的内存池要开倍。
代码
#include<bits/stdc++.h>
#define lb(x) (x&-x)
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll>
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void say() {}
void say_() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
int __=0;
_=0;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
__=1;
ch=getchar();
}
while(isdigit(ch))
{
_=_*10+ch-48;
ch=getchar();
}
_=__?-_:_;
read(oth...);
}
template <typename T>
void Out(T _)
{
if(_<0)
{
putchar('-');
_=-_;
}
if(_>=10)
Out(_/10);
putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void say(T _, T2... oth)
{
Out(_);
putchar('\n');
say(oth...);
}
template <typename T, typename... T2>
inline void say_(T _, T2... oth)
{
Out(_);
putchar(' ');
say_(oth...);
}
/*#################################*/
const ll N=1E5+10;
ll n,q,ans,cnt;
ll a[N],root[N],BIT[N],fir[N],sec[N],loc[N];
struct Node{
ll l,r,val;
}t[60*N];
void modify(ll l,ll r,ll &cur,ll pos)
{
if(!cur)
cur=++cnt;
++t[cur].val;
if(l==r)
return;
ll mid=(l+r)>>1;
if(pos<=mid)
modify(l,mid,t[cur].l,pos);
else
modify(mid+1,r,t[cur].r,pos);
}
ll query(ll l,ll r,ll cur,ll al,ll ar)
{
if(al<=l && ar>=r)
return t[cur].val;
ll mid=(l+r)>>1;
ll ret=0;
if(al<=mid)
ret+=query(l,mid,t[cur].l,al,ar);
if(ar>mid)
ret+=query(mid+1,r,t[cur].r,al,ar);
return ret;
}
ll find(ll L,ll R,ll l,ll r)
{
if(l>r)
return 0;
ll ret=0;
while(L)
{
ret-=query(1,n,root[L],l,r);
L-=lb(L);
}
while(R)
{
ret+=query(1,n,root[R],l,r);
R-=lb(R);
}
return ret;
}
ll SUM(ll x)
{
ll ret=0;
while(x)
{
ret+=BIT[x];
x-=lb(x);
}
return ret;
}
void ADD(ll x,ll val)
{
while(x<=n)
{
BIT[x]+=val;
x+=lb(x);
}
}
int main()
{
read(n,q);
rep(i,1,n)
{
read(a[i]);
loc[a[i]]=i;
}
rep(i,1,n)
{
fir[i]=SUM(n)-SUM(a[i]);
ans+=fir[i];
ADD(a[i],1);
}
memset(BIT,0,sizeof(BIT));
for(int i=n;i>=1;--i)
{
sec[i]=SUM(a[i]-1);
ADD(a[i],1);
}
ll x;
rep(i,1,q)
{
say(ans);
read(x);
ll pos=loc[x];
ll res=fir[pos]+sec[pos];
res-=find(0,pos-1,x+1,n)+find(pos,n,1,x-1);
ans-=res;
while(pos<=n)
{
modify(1,n,root[pos],x);
pos+=lb(pos);
}
}
}