【BZOJ3333】排队计划(树状数组+线段树)

点此看题面

大致题意: 给定一个序列(a_i),每次把(isim n)中小于等于(a_i)的数排序并放回原位置,求所有操作前及每一操作后的逆序对个数。

性质

众所周知,排序的题目中必然有许多有趣的性质。

我们定义一个数的贡献为它为较大值的逆序对个数

可以发现,根据此题的排序方式,不被排序的数与被排序的数之间的相对大小是不变的,也就是说,不被排序的数的贡献不会被改变。

而所有被排序的数,由于小于它的数必然都到了它的前面,因此贡献相当于被清零了。

线段树

求初始贡献可以直接用树状数组,然后我们还需要一个数据结构,能实现对一段区间内小于等于某数的数进行修改。

乍一看,两重限制,似乎是树套树?

实际上, 由于每次修改是清零,因此每个数只需被修改一次即可。

那么,如果我们每次清零时同时给这个位置上的高度赋一个极大值,就能够保证每个位置只被修改一次。

于是,只要用线段树就能维护了。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define LL long long
using namespace std;
int n,a[N+5],s[N+5],dc,dv[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		#define D isdigit(c=tc())
		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
	public:
		I FastIO() {A=B=FI,C=FO,E=FO+FS;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('
');}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
class SegmentTree//线段树
{
	private:
		#define PT CI l=1,CI r=n,CI rt=1
		#define LT l,mid,rt<<1
		#define RT mid+1,r,rt<<1|1
		#define PU(x) (O[x].V=O[x<<1].V+O[x<<1|1].V,O[x].H=min(O[x<<1].H,O[x<<1|1].H))
		struct node {LL V;int H;}O[N<<2];
	public:
		I void Build(PT)//建树
		{
			if(l==r) return (void)(O[rt].V=s[l],O[rt].H=a[l]);
			int mid=l+r>>1;Build(LT),Build(RT),PU(rt);
		}
		I void U(CI s,CI x,PT)//修改
		{
			if(l==r) return (void)(O[rt].V=0,O[rt].H=dc+1);int mid=l+r>>1;//清零的同时赋一个极大值
			s<=mid&&O[rt<<1].H<=x&&(U(s,x,LT),0),O[rt<<1|1].H<=x&&(U(s,x,RT),0),PU(rt);//如果存在小于等于x的数就去修改
		}
		I LL Q() {return O[1].V;}//询问整个序列和
}S;
struct TreeArray//树状数组
{
	int v[N+5];I void U(RI x) {W(x<=dc) ++v[x],x+=x&-x;}//修改
	I int Q(RI x,RI t=0) {W(x) t+=v[x],x-=x&-x;return t;}//询问
}T;
int main()
{
	RI Qt,i,x;for(F.read(n),F.read(Qt),i=1;i<=n;++i) F.read(a[i]),dv[i]=a[i];
	for(sort(dv+1,dv+n+1),dc=unique(dv+1,dv+n+1)-dv-1,i=n;i;--i)//离散化
		a[i]=lower_bound(dv+1,dv+dc+1,a[i])-dv,s[i]=T.Q(a[i]-1),T.U(a[i]);//求初始贡献
	S.Build(),F.writeln(S.Q());W(Qt--) F.read(x),S.U(x,a[x]),F.writeln(S.Q());//每次将被排序的数贡献清零
	return F.clear(),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3333.html