zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    Day10 python基础---函数进阶
    Day9 python基础---函数初识
    Day8 python基础
    Day7 python基础
    Day6 python基础
    Day5 python基础
    Day4 python基础
    Day3 python基础
    TensorFlow学习笔记5-概率与信息论
    TensorFlow学习笔记6-数值计算基础
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3333.html
Copyright © 2011-2022 走看看