Description
Solution
非常巧妙地区分了我这种不会算复杂度的菜鸡选手
先离散一下树状数组求逆序对,记f[i]为下标大于i且小于a[i]的数字数量,显然答案就是f[]的和,并且每次修改都会使一部分f变为0
考虑用线段树维护这个过程,由于每个数字只会被改变一次,因此是均摊nlogn的
记得要开LL
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define lowibt(x) ((x)&(-(x)))
typedef long long LL;
const int INF=1000000009;
const int N=500005;
int mn[N*4],f[N];
int a[N],b[N],c[N*2],lim;
LL ans;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void add(int x) {
for (;x<=lim;x+=lowibt(x)) c[x]++;
}
int ask(int x) {
int ret=0;
for (;x;x-=lowibt(x)) ret+=c[x];
return ret;
}
void modify(int now,int tl,int tr,int l,int r,int x) {
if (mn[now]>x) return ;
if (tl==tr) {
mn[now]=INF;
ans-=f[tl];
return ;
}
int mid=(tl+tr)>>1;
if (r<=mid) modify(now<<1,tl,mid,l,r,x);
else if (l>mid) modify(now<<1|1,mid+1,tr,l,r,x);
else {
modify(now<<1,tl,mid,l,mid,x);
modify(now<<1|1,mid+1,tr,mid+1,r,x);
}
mn[now]=std:: min(mn[now<<1],mn[now<<1|1]);
}
void build_tree(int now,int tl,int tr) {
mn[now]=INF;
if (tl==tr) {
mn[now]=b[a[tl]];
return ;
}
int mid=(tl+tr)>>1;
build_tree(now<<1,tl,mid);
build_tree(now<<1|1,mid+1,tr);
mn[now]=std:: min(mn[now<<1],mn[now<<1|1]);
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
int n=read(),m=read();
rep(i,1,n) b[i]=a[i]=read();
std:: sort(b+1,b+n+1);
lim=std:: unique(b+1,b+n+1)-b-1;
drp(i,n,1) {
a[i]=std:: lower_bound(b+1,b+lim+1,a[i])-b;
add(a[i]); f[i]=ask(a[i]-1);
ans+=f[i];
}
build_tree(1,1,n);
printf("%lld\n", ans);
for (;m--;) {
int x=read();
modify(1,1,n,x,n,b[a[x]]);
printf("%lld\n", ans);
}
return 0;
}