• 【BZOJ4540】 [HNOI2016] 序列(莫队)


    点此看题面

    大致题意: 求出一个序列的一段区间中所有子序列最小值之和。

    莫队

    这道题其实是一道莫队题。

    但是需要大量的预处理。

    预处理

    先考虑预处理两个数组(lst_i)(nxt_i),分别表示在第(i)个元素左边、右边第一个小于它的元素的位置

    这可以直接用单调栈实现(O(n))预处理。

    然后,我们考虑预处理出(sum_i)(lst_i)两个数组,分别存储([1,i])区间右侧插入一个数的代价([i,n])区间左侧插入一个数的代价,即(sum_{k=1}^i Min_{x=1}^k a_x)(sum_{k=i}^n Min_{x=k}^n a_x)

    这可以在预处理(lst_i)(nxt_i)的同时这样预处理:

    sum[i]=sum[lst[i]]+1LL*a[i]*(i-lst[i]);//suf数组同理
    

    区间移动

    接下来,我们要考虑如何处理区间移动(以在左边插入一个数为例)。

    首先,我们找到这个区间内最小的数所在的位置(p),显然,第([p,R])的位置与(L)所构成的区间的最小值都是(a_p)

    然后就考虑([L,p-1])区间内的贡献。

    考虑到([L,p])区间内(a_p)最小,因此(suf_L)值肯定是从(suf_p)经过若干次转移得来的。

    因此它就满足前缀和性质了,可以直接通过(suf_L-suf_p)来计算贡献。

    而其他的区间移动操作也是同理的,具体实现详见代码。

    代码

    #include<bits/stdc++.h> 
    #define N 100000 
    #define LL long long 
    #define INF 1e9 
    #define Gmin(x,y) (x>(y)&&(x=(y))) 
    using namespace std; 
    int n,query_tot,a[N+5]; 
    class Class_FIO 
    { 
        private: 
            #define Fsize 100000 
            #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++) 
            #define pc(ch) (void)(FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch)) 
            int f,Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize]; 
        public: 
            Class_FIO() {A=B=Fin;} 
            inline void read(int &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;} 
            inline void writeln(LL x) {if(!x) return pc('0'),pc('
    ');x<0&&(pc('-'),x=-x);while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);pc('
    ');} 
            inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;} 
    }F; 
    class Class_CaptainMotao 
    { 
        private: 
            #define AddLeft() {register int p=RMQ.GetMin(--L,R);res+=1LL*a[p]*(R-p+1)+I.suf[L]-I.suf[p];} 
            #define AddRight() {register int p=RMQ.GetMin(L,++R);res+=1LL*a[p]*(p-L+1)+I.sum[R]-I.sum[p];} 
            #define DelLeft() {register int p=RMQ.GetMin(L,R);res-=1LL*a[p]*(R-p+1)+I.suf[L++]-I.suf[p];} 
            #define DelRight() {register int p=RMQ.GetMin(L,R);res-=1LL*a[p]*(p-L+1)+I.sum[R--]-I.sum[p];} 
            int S;LL ans[N+5]; 
            class Class_Initer//初始化
            { 
                private: 
                    int Top,lst[N+5],nxt[N+5],Stack[N+5]; 
                public: 
                    LL sum[N+5],suf[N+5]; 
                    inline void Init() 
                    { 
                        register int i; 
                        for(i=1;i<=n;++i) {while(Top&&a[Stack[Top]]>=a[i]) --Top;lst[i]=Stack[Top],Stack[++Top]=i,sum[i]=sum[lst[i]]+1LL*a[i]*(i-lst[i]);} 
                        for(Stack[Top=0]=n+1,i=n;i;--i) {while(Top&&a[Stack[Top]]>=a[i]) --Top;nxt[i]=Stack[Top],Stack[++Top]=i,suf[i]=suf[nxt[i]]+1LL*a[i]*(nxt[i]-i);} 
                    } 
            }I; 
            class Class_RMQ//RMQ
            { 
                private: 
                    #define LogN 20 
                    int Log2[N+5]; 
                    struct RMQ_data 
                    { 
                        int val,pos; 
                        inline friend bool operator < (RMQ_data x,RMQ_data y) {return x.val<y.val;} 
                        RMQ_data(int x=0,int y=0):val(x),pos(y){} 
                    }Min[N+5][LogN+5]; 
                public: 
                    inline void Init() 
                    { 
                        register int i,j; 
                        for(i=2;i<=n;++i) Log2[i]=Log2[i>>1]+1; 
                        for(i=1;i<=n;++i) Min[i][0]=RMQ_data(a[i],i); 
                        for(j=1;(1<<j-1)<=n;++j) for(i=1;i+(1<<j-1)<=n;++i) Min[i][j]=min(Min[i][j-1],Min[i+(1<<j-1)][j-1]); 
                    } 
                    inline int GetMin(int l,int r) {register int k=Log2[r-l+1];return min(Min[l][k],Min[r-(1<<k)+1][k]).pos;} 
            }RMQ; 
            struct Query 
            { 
                int l,r,pos,bl; 
                inline friend bool operator < (Query x,Query y) {return x.bl^y.bl?x.bl<y.bl:(x.bl&1?x.r<y.r:x.r>y.r);} 
            }q[N+5]; 
        public: 
            inline void Solve() 
            { 
                register int i,L=1,R=0;register LL res=0; 
                for(I.Init(),RMQ.Init(),S=sqrt(n),i=1;i<=query_tot;++i) F.read(q[q[i].pos=i].l),F.read(q[i].r),q[i].bl=(q[i].l-1)/S+1; 
                for(sort(q+1,q+query_tot+1),i=1;i<=query_tot;++i)//处理询问
                { 
                    while(R<q[i].r) AddRight(); 
                    while(L>q[i].l) AddLeft(); 
                    while(R>q[i].r) DelRight(); 
                    while(L<q[i].l) DelLeft(); 
                    ans[q[i].pos]=res; 
                } 
                for(i=1;i<=query_tot;++i) F.writeln(ans[i]); 
            } 
    }C; 
    int main() 
    { 
        register int i; 
        for(F.read(n),F.read(query_tot),i=1;i<=n;++i) F.read(a[i]); 
        return C.Solve(),F.clear(),0; 
    } 
    
  • 相关阅读:
    VBA值列选取与复制,赋值
    Processing的条件式
    VBA之四给程序自动加行号
    自上而下的语法分析
    Processing绘制四边形
    Processing的代码编写流程
    Processing编程语言简介
    follow集的求解
    Processing函数与循环
    在UBUNTU下用ruby求得网卡地址IP地址和用户名
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4540.html
Copyright © 2020-2023  润新知