题目大意:给定一个序列h,每次选择一个位置,把这个位置之后所有小于等于这个数的数抽出来,排序,再插回去,求每次操作后的逆序对数
题解:用树状数组求出第一问答案,记 hi 开头的逆序对数= ∑j=i+1n[hj<hi]
设操作位置为pos,选出的数集为{S}
分析操作对不在{S}集中元素的影响
对于
∀i<pos
,{S}中元素与i的相对位置不变,逆序对数不变
对于
∀i>pos
且
hi>hpos
,{S}中元素对i等价,无论对{S}如何排序均不会改变逆序对数
综上,每次操作只会影响 i>pos 且 hi<hpos 的元素,也即使得所有以{S}中元素开头的逆序对消失,且不可逆
以 hi 建立线段树,维护区间最小值
对于操作pos,查询出[p,n]的最小值所在位置mip(只需要线段树存位置就可以了,相等优先选靠后的),答案减掉 fmip ,把 fmip 清零, hmip 赋为INF,直到 hp=INF ,这说明所有{S}中元素操作完毕
每个数只会被找一次,均摊 O(n logn)
我的收获:操作复杂度分析,分类讨论大法
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int M=500005;
const int INF=1e9;
int n,m;
int a[M],z[M],f[M];
long long ans;
struct Fenwick_Tree{
int c[M];
void updata(int x,int v){
for(int i=x;i<=n;i+=i&(-i))
c[i]+=v;
}
int query(int x){
int ret=0;
for(int i=x;i>0;i-=i&(-i))
ret+=c[i];
return ret;
}
}FT;
struct Segment_Tree{
#define ls x<<1
#define rs x<<1|1
#define lson l,m,x<<1
#define rson m+1,r,x<<1|1
#define root 1,n,1
int mi[M<<2];
inline void pushup(int x){mi[x]=a[mi[ls]]<a[mi[rs]]?mi[ls]:mi[rs];}
void build(int l,int r,int x)
{
if(l==r){mi[x]=l;return ;}
int m=l+r>>1;
build(lson),build(rson);
pushup(x);
}
void updata(int P,int v,int l,int r,int x)
{
if(l==r){a[l]=v;return ;}
int m=l+r>>1;
if(P<=m) updata(P,v,lson);
if(P>m) updata(P,v,rson);
pushup(x);
}
int query(int L,int R,int l,int r,int x)
{
if(L<=l&&r<=R) return mi[x];
int m=l+r>>1,lc=-1,rc=-1;
if(L<=m) lc=query(L,R,lson);
if(R>m) rc=query(L,R,rson);
if(lc!=-1&&a[lc]<a[rc]) return lc;
return rc;
}
}ST;
void hashit()
{
for(int i=1;i<=n;i++) z[i]=a[i];
sort(z+1,z+1+n);
int cnt=unique(z+1,z+1+n)-z-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(z+1,z+1+cnt,a[i])-z+1;
}
void calcnow()
{
for(int i=n;i>=1;i--){
FT.updata(a[i],1);
ans+=f[i]=FT.query(a[i]-1);
}
printf("%lld\n",ans);
}
void calcmove()
{
ST.build(root);
while(m--)
{
int p;
scanf("%d",&p);
while(a[p]!=INF)
{
int mip=ST.query(p,n,root);
ans-=f[mip],f[mip]=0;
ST.updata(mip,INF,root);
}
printf("%lld\n",ans);
}
}
void work()
{
calcnow();
calcmove();
}
void init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
hashit();
}
int main()
{
init();
work();
return 0;
}