2019.01.22 bzoj3333: 排队计划(逆序对+线段树)

版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/86600285

传送门
题意简述:给出一个序列,支持把 p p ~ n n 中所有小于等于 a p a_p 的‘扯出来排序之后再放回去,要求动态维护全局逆序对。


思路:我们令 f i f_i 表示第 i i 个位置之后比它大的数的个数,考虑到一个数在排一次序之后 f i f_i 就变成了 0 0 ,因此等价于每个位置最多修改一次,我们用树状数组先求出 f i f_i ,然后上线段树来暴力修改即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=5e5+5,inf=1e9;
int n,m,f[N],a[N],mp[N];
ll sum=0;
namespace Bit{
	int bit[N],lim;
	inline int lowbit(int x){return x&-x;}
	inline void update(int x){for(ri i=x;i<=lim;i+=lowbit(i))++bit[i];}
	inline int query(int x){int ret=0;for(ri i=x;i;i-=lowbit(i))ret+=bit[i];return ret;}
}
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (l+r>>1)
	int mn[N<<2];
	inline void pushup(int p){mn[p]=min(mn[lc],mn[rc]);}
	inline void update(int p,int l,int r,int ql,int qr,int k){
		if(mn[p]>k)return;
		if(l==r){mn[p]=inf,sum-=f[l];return;}
		if(qr<=mid)update(lc,l,mid,ql,qr,k);
		else if(ql>mid)update(rc,mid+1,r,ql,qr,k);
		else update(lc,l,mid,ql,mid,k),update(rc,mid+1,r,mid+1,qr,k);
		pushup(p);
	}
	inline void build(int p,int l,int r){
		if(l==r){mn[p]=mp[a[l]];return;}
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
}
int main(){
	n=read(),m=read();
	for(ri i=1;i<=n;++i)a[i]=mp[i]=read();
	sort(mp+1,mp+n+1),Bit::lim=unique(mp+1,mp+n+1)-mp-1;
	for(ri i=n;i;--i){
		a[i]=lower_bound(mp+1,mp+Bit::lim+1,a[i])-mp;
		Bit::update(a[i]),f[i]=Bit::query(a[i]-1),sum+=f[i];
	}
	SGT::build(1,1,n),cout<<sum<<'\n';
	for(ri i=1,x;i<=m;++i)x=read(),SGT::update(1,1,n,x,n,mp[a[x]]),cout<<sum<<'\n';
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/86600285