• 【BZOJ1367】[Baltic2004] sequence(左偏树维护中位数)


    点此看题面

    大致题意: 给定一个序列(a_i),求一个递增序列(b_i)使得(sum_{i=1}^n|a_i-b_i|)最小。

    递增( ightarrow)不降

    显然这题中递增序列这个条件让人很难受。

    因此,我们对于每个(a_i),给它减去一个(i)(b_i)同理),这样一来(b_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 1000000
    #define swap(x,y) (x^=y^=x^=y)
    using namespace std;
    int n,a[N+5],S[N+5],P[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 write(Ty x,Con char& y) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc(y);}
    		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
    		#undef D
    }F;
    class LeftistTree//左偏树
    {
    	private:
    		struct node {int Sz,D,S[2];}O[N+5];
    	public:
    		I int operator [] (CI x) Con {return O[x].Sz;} 
    		I void Init() {for(RI i=1;i<=n;++i) O[i].Sz=1;}
    		I int Merge(RI x,RI y)//合并
    		{
    			if(!x||!y) return x|y;a[x]<a[y]&&swap(x,y),
    			O[O[x].S[1]=Merge(O[x].S[1],y)].D>O[O[x].S[0]].D&&swap(O[x].S[0],O[x].S[1]);
    			return O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+1,O[x].D=O[O[x].S[1]].D+1,x;
    		}
    		I int Pop(CI x) {return Merge(O[x].S[0],O[x].S[1]);}//弹出堆顶
    }L;
    int main()
    {
    	RI i,j;for(F.read(n),i=1;i<=n;++i) F.read(a[i]),a[i]-=i;//减去下标以改递增为不降
    	RI k,T=0;for(L.Init(),i=1;i<=n;++i)
    	{
    		k=i;W(T&&a[k]<a[S[T]]) k=L.Merge(k,S[T--]),L[k]>(i-P[T]+1>>1)&&(k=L.Pop(k));//单调栈,用左偏树维护中位数
    		S[++T]=k,P[T]=i;//加入栈中
    	}
    	long long ans=0;for(i=j=1;i<=n;++i) ans+=abs(a[i]-a[S[j]]),P[j]==i&&++j;
    	return printf("%lld",ans),0;
    //	洛谷需要输出b。。。
    //	for(F.write(ans,'
    '),i=j=1;i<=n;++i) F.write(a[S[j]]+i," 
    "[i==n]),P[j]==i&&++j;
    //	return F.clear(),0;
    }
    
  • 相关阅读:
    Python运维开发之路《WEB框架:Django》
    Python运维开发之路《函数进阶》
    Python运维开发之路《编程》
    Python运维开发之路《模块》
    Linux系统运维之Web服务器Nginx安装
    Python运维开发之路《高阶函数》
    Python运维开发之路《函数》
    <读书笔记>软件调试之道 :实证方法
    (知识分享)软硬件调试九法:第九条规则 如果你不修复一个bug,它将永远 存在
    (知识分享)软硬件调试九法:第八条规则 求助他人, 获得全新观点
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1367.html
Copyright © 2020-2023  润新知