• 单调栈+线段树——cf1220F


     首先考虑初始排列,pi会让周围所有比其大的元素深度+1,所以要求每个点的深度,只要其被覆盖了几次即可

    这个覆盖可以通过处理每个元素的左右边界(单调栈O(n))+线段树区间更新(Ologn(n))来做

    然后是将排列最左边一个元素移到最右边:

      在左边删元素pi,只会让pi右边所有比其大的元素深度-1,

      在右边加上元素pi,会让pi左边比其大的元素深度+1

    这种循环左右移动,需要频繁更改线段树的下标,不好操作,所以我们一开始直接用一个两倍的数组来建立线段树,只要查询时查询长度为n即可

    #include<bits/stdc++.h>
    #include<stack>
    using namespace std;
    #define N 400005
    
    int n,a[N],L[N],R[N];
    
    void work(){
        stack<int>stk;    
        a[2*n+1]=0;
        for(int i=1;i<=2*n+1;i++){
            if(stk.empty()){stk.push(i);continue;}
            while(stk.size() && a[stk.top()]>=a[i]){
                R[stk.top()]=i-1;
                stk.pop();
            }
            stk.push(i);
        }
        
        while(stk.size())stk.pop();
        a[0]=0;
        for(int i=2*n;i>=0;i--){
            if(stk.empty()){stk.push(i);continue;}
            while(stk.size() && a[stk.top()]>=a[i]){
                L[stk.top()]=i+1;
                stk.pop();
            }
            stk.push(i);
        }
    }
    
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    int lazy[N<<2],Max[N<<2];
    void pushdown(int rt){
        if(lazy[rt]){
            lazy[rt<<1]+=lazy[rt];Max[rt<<1]+=lazy[rt];
            lazy[rt<<1|1]+=lazy[rt];Max[rt<<1|1]+=lazy[rt];
            lazy[rt]=0;
        }
    }
    void update(int L,int R,int v,int l,int r,int rt){
        if(L<=l && R>=r){Max[rt]+=v;lazy[rt]+=v;return;}
        int m=l+r>>1;
        pushdown(rt);
        if(L<=m)update(L,R,v,lson);
        if(R>m)update(L,R,v,rson);
        Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
    }
    int query(int L,int R,int l,int r,int rt){
        if(L<=l && R>=r)return Max[rt];
        int m=l+r>>1,res=0;
        pushdown(rt);
        if(L<=m)res=max(res,query(L,R,lson));
        if(R>m)res=max(res,query(L,R,rson));
        return res;
    }
    
    int main(){
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++)a[i+n]=a[i];
    
        work();//处理出每个元素可以控制的范围 
        
        for(int i=1;i<=n;i++)
            update(L[i],R[i],1,1,2*n,1);
        
        int ans=query(1,n,1,2*n,1),pos=0;
        for(int i=1;i<n;i++){
            int s=i,t=i+n;
            update(L[s],R[s],-1,1,2*n,1);//把s从左端删掉 
            update(L[t],R[t],1,1,2*n,1);//把t加入右端 
            int res=query(s+1,t,1,2*n,1);
            if(res<ans){
                ans=res,pos=i;
            } 
        }
        
        cout<<ans<<" "<<pos<<'
    ';
        return 0;
    }
  • 相关阅读:
    使用QTM 博客客户端
    sdut 2080 最长公共子序列问题
    sdut 1730 数字三角形问题
    HDOJ 1905 Pseudoprime numbers(模运算)
    HDU 1285确定比赛名次(拓补排序)
    HDU 2094产生冠军(map)
    HDOJ 1228 A+B(map水题)
    HDOJ 1713 相遇周期 (最大公约数与最小公倍数)
    HDOJ 2098 分拆素数和(筛选法求素数)
    (转)最大子序列和问题
  • 原文地址:https://www.cnblogs.com/zsben991126/p/11625592.html
Copyright © 2020-2023  润新知