\(Description\)
对于序列\(A\),它的逆序对数定义为满足\(i<j\),且\(A_{i}\)>\(A_{j}\)的数对\((i,j)\)的个数。给\(1\)到\(n\)的一个排列,按照某种顺序依次删除\(m\)个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
\(Input\)
输入第一行包含两个整数\(n\)和\(m\),即初始元素的个数和删除的元素个数。以下\(n\)行每行包含一个\(1\)到\(n\)之间的正整数,即初始排列。以下\(m\)行每行一个正整数,依次为每次删除的元素。
\(Output\)
输出包含\(m\)行,依次为删除每个元素之前,逆序对的个数。
\(Sample Input\)
5 4
1
5
3
4
2
5
1
4
2
\(Sample Output\)
5
2
2
1
\(HINT\)
样例解释
\((1,5,3,4,2)⇒(1,3,4,2)⇒(3,4,2)⇒(3,2)⇒(3)。\)
\(M≤N≤100000\)
思路
我们第一眼看上去,哇,三维偏序
再看一眼,哇,待修三维偏序
于是我们选择用树套树实现
在这里我们选择树状数组套权值线段树
但是我们发现,如果直接开点可能会爆空间?!
那就动态开点线段树咯
我们在树状数组每个节点开一棵线段树,维护每个区间权值不同的数的个数
而树状数组用前缀和维护权值大于的关系
最后每删去一个数就减去这个数对逆序对的贡献
这里特殊把\(getans\)拎出来讲讲
long long getans(int x,int p)
{
return query(p,n)-query(p,x)+query(n,x)-query(p,x);
}
这是在删除一个数时维护对逆序对的贡献的代码
\(query(x,y)\)是查询下标小于\(x\)的数中值小于\(y\)的数的个数
于是,上面这段代码我们可以分为两段理解
\(1.query(p,n)-query(p,x)\)表示在小于\(p\)的下标中,值在\(x\)到\(n\)中的数的个数
\(2.query(n,x)-query(p,x)\)表示在\(p\)到\(n\)的下标中,值小于\(x\)的数的个数
显而易见,这两个加起来就是一个数对逆序对的贡献,减去即可
完整代码:
#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using namespace std;
const int N=100010;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+(ch^48);
ch=getchar();
}
return x*f;
}
int n,m;
int pos[N];
int c[N];
int rt[N];
int cnt=0;
struct tree
{
int sum,ch[2];
}t[N*100];
void add(int x,int y)
{
for(;x<=n;x+=lowbit(x))c[x]+=y;
}
void modify(int &k,int l,int r,int x,int val)
{
if(!k)k=++cnt;
t[k].sum+=val;
//动态开点
if(l==r)return ;
int mid=(l+r)>>1;
if(x<=mid)modify(t[k].ch[0],l,mid,x,val);
else modify(t[k].ch[1],mid+1,r,x,val);
}
void ins(int x,int p,int v)
{
for(;x<=n;x+=lowbit(x))modify(rt[x],1,n,p,v);
}
long long getnum(int x)
{
long long ans=0;
for(;x;x-=lowbit(x))ans+=c[x];
return ans;
}
long long ask(int k,int l,int r,int p)//查询在区间内小于p的数
{
if(l==r)return t[k].sum;
int mid=(l+r)>>1;
if(p<=mid)return ask(t[k].ch[0],l,mid,p);
return t[t[k].ch[0]].sum+ask(t[k].ch[1],mid+1,r,p);
}
long long query(int x,int p)
{
long long ans=0;
for(;x;x-=lowbit(x))ans+=ask(rt[x],1,n,p);
return ans;
}
long long getans(int x,int p)
{
return query(p,n)-query(p,x)+query(n,x)-query(p,x);
}
int main()
{
n=read(),m=read();
int x;
long long ans=0ll;
for(int i=1;i<=n;i++)
{
x=read();
pos[x]=i;
ans+=getnum(n)-getnum(x);//在没有修改之前,直接用树状数组前缀和查询大于x的数个数,满足逆序对
add(x,1);//将x的树状数组+1
ins(i,x,1);//加入线段树中
}
for(int i=1;i<=m;i++)
{
printf("%lld\n",ans);
x=read();
ans-=getans(x,pos[x]);//减去这个点对逆序对的贡献
ins(pos[x],x,-1);//在线段树中-1
}
return 0;
}