zoukankan      html  css  js  c++  java
  • 连续区间计数 [线段树, 单调栈]

    连续区间计数



    color{grey}{最初想法}

    一个区间合法当且仅当 max_vmin_v==rlmax\_v-min\_v == r-l, 移项得 min_v==max_v(rl)min\_v == max\_v - (r-l),
    可以想到枚举 min_v=A[i]min\_v = A[i], 记录左边第一个比 ii 小的位置和右边第一个比 ii 小的位置,
    分别记为 Ld[i],Rd[i]Ld[i], Rd[i], 然后在 (Ld[i],Rd[i])(Ld[i], Rd[i]) 中枚举 左端点属于 (Ld[i],p](Ld[i], p], 右端点属于 (p,Rd[i])(p, Rd[i]),
    直接裸算时间复杂度期望 O(nlog2n)O(nlog^2 n),
    加上枚举右端点时的 “可行性剪枝”, 和对 A[i]A[i+1]=1|A[i]-A[i+1]|=1 连续数字的加速, 跑随机数据 0.3s0.3s 可出答案 .
    codecode


    color{red}{正解部分}

    从左向右 枚举右端点 rr, 检查 rr 的左边有多少 ll 满足 r=max_vmin_v+lr = max\_v-min\_v + l,
    其中 max_v,min_vmax\_v, min\_v 都是在 [l,r][l, r] 区间意义下的,

    • 这里有个 性质: max_vmin_vrlmax_vmin_v+lrmax\_v-min\_v geq r-l ightarrow max\_v-min\_v + l geq r .
      有了这个, 就可以考虑使用 线段树 维护 max_vmin_v+lmax\_v-min\_v+l区间最小值,
      因为只有可能 最小值 满足 max_vmin_v+l==rmax\_v-min\_v+l == r .

    考虑 rr 向右移动一位对 l[1,r+1]l ∈ [1, r+1] 的影响,
    此时 A[r+1]A[r+1] 可能成为关于 [l,r+1][l, r+1] 的新的 最大值, 也可能成为新的 最小值,
    而影响的区间又是连续的, 考虑使用 单调栈 维护,

    具体来说: 以维护 最大值 为例, 建立一个 单调递减单调栈, 中存下标,
    在弹出 栈顶 时在 线段树 中更新 栈顶次栈顶 大小的 左端点们 代表的区间 .
    单调递增单调栈 也按此方法维护 最小值 .

    对于 rr, 统计 线段树 的区间 [1,r][1, r] 中有多少 最小值 等于 rr 即可 .


    color{red}{实现部分}

    #include<bits/stdc++.h>
    #define reg register
    
    int read(){
            char c;
            int s = 0, flag = 1;
            while((c=getchar()) && !isdigit(c))
                    if(c == '-'){ flag = -1, c = getchar(); break ; }
            while(isdigit(c)) s = s*10 + c-'0', c = getchar();
            return s * flag;
    }
    
    const int maxn = 300005;
    
    int N;
    int top[2];
    int A[maxn];
    int stk[2][maxn];
    
    struct Segment_Tree{
    
            struct Node{ int l, r, tag, min_v, min_n; } T[maxn<<3];
    
            void Push_up(int k){ 
                    T[k].min_v = std::min(T[k<<1].min_v, T[k<<1|1].min_v);
                    T[k].min_n = 0;
                    if(T[k].min_v == T[k<<1].min_v) T[k].min_n += T[k<<1].min_n;
                    if(T[k].min_v == T[k<<1|1].min_v) T[k].min_n += T[k<<1|1].min_n;
            }
    
            void Build(int k, int l, int r){
                    T[k].l = l, T[k].r = r;
                    if(l == r){ T[k].min_v = l, T[k].min_n = 1; return ; }
                    int mid = l+r >> 1;
                    Build(k<<1, l, mid), Build(k<<1|1, mid+1, r), Push_up(k);
            }
    
            void Push_down(int k){
                    T[k].min_v += T[k].tag;
                    int lt = k << 1, rt = k<<1 | 1;
                    T[lt].tag += T[k].tag, T[rt].tag += T[k].tag;
                    T[k].tag = 0;
            }
    
            void Modify(int k, const int &ql, const int &qr, const int &v){
                    int l = T[k].l, r = T[k].r;
                    if(T[k].tag) Push_down(k);
                    if(l > qr || r < ql) return ;
                    if(ql <= l && r <= qr){ T[k].tag += v, Push_down(k); return ; }
                    int mid = l+r >> 1;
                    Modify(k<<1, ql, qr, v), Modify(k<<1|1, ql, qr, v);
                    Push_up(k);
            }
    
            int Query(int k, const int &ql, const int &qr){
                    int l = T[k].l, r = T[k].r;
                    if(T[k].tag) Push_down(k);
                    if(ql <= l && r <= qr){ return T[k].min_v==qr?T[k].min_n:0; }
                    int mid = l+r >> 1, s = 0;
                    if(ql <= mid) s += Query(k<<1, ql, qr);
                    if(qr > mid) s += Query(k<<1|1, ql, qr);
                    return s;
            }
    
    } seg_t;
    
    int main(){
            N = read();
            for(reg int i = 1; i <= N; i ++) A[i] = read();
            seg_t.Build(1, 1, N);
            long long Ans = 0;
            for(reg int r = 1; r <= N; r ++){
                    while(top[0] && A[stk[0][top[0]]] > A[r]){ // 1, 2, 3
                            seg_t.Modify(1, stk[0][top[0]-1]+1, stk[0][top[0]], A[stk[0][top[0]]] - A[r]);
                            -- top[0];
                    }
                    stk[0][++ top[0]] = r;
                    while(top[1] && A[stk[1][top[1]]] < A[r]){
                            seg_t.Modify(1, stk[1][top[1]-1]+1, stk[1][top[1]], A[r] - A[stk[1][top[1]]]);
                            -- top[1];
                    }
                    stk[1][++ top[1]] = r;
                    Ans += seg_t.Query(1, 1, r);
            }
            printf("%lld
    ", Ans);
            return 0;
    }
    
  • 相关阅读:
    tomcat 配置ssi
    oracle exp imp 导入 正在跳过表 plsql 导入表 成功终止 数据 被导入
    oracle 创建数据表空间和用户
    移动端开发来个总结吧
    ssl四次握手
    面试-布局
    typeof的原理?
    马上面试了就,复习一下排序
    关于webview无法设置页面标题
    关于let的生命提升
  • 原文地址:https://www.cnblogs.com/zbr162/p/11822478.html
Copyright © 2011-2022 走看看