\(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; }