码迷,mamicode.com
首页 > 其他好文 > 详细

【CF997E】Good Subsegments (线段树+单调栈)

时间:2018-10-08 23:14:33      阅读:414      评论:0      收藏:0      [点我收藏+]

标签:好的   define   type   ==   个数   com   长度   turn   href   

Description

原题链接
给你一个长度为\(n\)的排列\(~P\),定义一段子区间是好的,当且仅当这个子区间内的值构成了连续的一段。例如对于排列\(\{1,3,2 \}\)\([1, 1], [2, 2], [3, 3], [2, 3], [1, 3]\)是好的区间。
\(q\)次询问,每次询问\(L,R\), 求有多少\(L \leq l \leq r \leq R\),满足\([l, r]\)是好的区间。\(1 \leq n, q \leq 1.2 \times 10 ^ 5\).

Solution

可以发现,区间\([l, r]\)是好的,当且仅当\(~(Max_{i = l}^{r} - Min_{i = l} ^ {r}) - (r - l) = 0\).
考虑维护一段区间上式的最小值(以下的最小值都指上式最小值),每一个最小值为\(0\)的位置都可以和当前的\(r\)组成一个好的区间。不难发现,一个右端点能产生的贡献为它左边的点的最小值为\(0\)的个数,于是可以在线段树上维护最小值和最小值个数,以及每个最小值为\(0\)的位置产生的贡献。
由于右边新加的点会对已有的点产生影响,考虑离线询问,按右端点排序,用两个单调栈分别维护当前\(Min\)\(Max\)。右端点右移时,势必会使整个区间的最小值减一,也势必会使其未右移前的右端点对答案产生一轮贡献,对每次处理右端点等于当前枚举点的答案。

Code

#include <bits/stdc++.h>

#define For(i, j, k) for (int i = j; i <= k; ++ i)
#define Forr(i, j, k) for (int i = j; i >= k; -- i)

using namespace std;

typedef long long ll;

inline int read() {
    int x = 0, p = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == ‘-‘) p = -1;
    for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * p;
}

inline void File() {
    freopen("CF997E.in", "r", stdin);
    freopen("CF997E.out", "w", stdout);
}

const int N = 1.2e5 + 10;
int n, q, a[N], s1[N], t1, s2[N], t2, tt = 1; ll ans[N];

struct Query {
    int id, l, r;
    bool operator < (const Query &rhs) const { return r < rhs.r; }
} Q[N];

namespace Segment_Tree {
#define lc (rt << 1)
#define rc (rt << 1 | 1)
#define mid (l + r >> 1)

    const int MAXN = N << 2;
    int mn[MAXN], tag[MAXN], t[MAXN]; ll sum[MAXN], tg[MAXN];
    
    inline void pushdown(int rt) {
        if (tag[rt]) {
            mn[lc] += tag[rt], mn[rc] += tag[rt];
            tag[lc] += tag[rt], tag[rc] += tag[rt];
            tag[rt] = 0;
        }

        if (tg[rt]) {
            if (mn[lc] == mn[rt]) sum[lc] += 1ll * t[lc] * tg[rt], tg[lc] += tg[rt];
            if (mn[rc] == mn[rt]) sum[rc] += 1ll * t[rc] * tg[rt], tg[rc] += tg[rt];
            tg[rt] = 0;
        }
    }

    inline void pushup(int rt) {
        t[rt] = 0, mn[rt] = min(mn[lc], mn[rc]);
        
        t[rt] = mn[rt] == mn[lc] ? t[rt] + t[lc] : t[rt];
        t[rt] = mn[rt] == mn[rc] ? t[rt] + t[rc] : t[rt];

        sum[rt] = sum[lc] + sum[rc];
    }

    inline void Build(int rt, int l, int r) {
        mn[rt] = l, t[rt] = 1;
        if (l ^ r) Build(lc, l, mid), Build(rc, mid + 1, r);
    }

    inline void update(int rt, int l, int r, int L, int R, int v) {
        if (L <= l && r <= R) { mn[rt] += v, tag[rt] += v; return ; }
        pushdown(rt); if (L <= mid) update(lc, l, mid, L, R, v);
        if (R > mid) update(rc, mid + 1, r, L, R, v); pushup(rt);
    }   

    inline ll query(int rt, int l, int r, int L, int R) {
        if (L <= l && r <= R) return sum[rt]; pushdown(rt);
        if (R <= mid) return query(lc, l, mid, L, R);
        if (L > mid) return query(rc, mid + 1, r, L, R);
        return query(lc, l, mid, L, R) + query(rc, mid + 1, r, L, R);
    }

#undef lc
#undef rc
#undef mid
}

int main() {
    File();

    using namespace Segment_Tree;

    n = read(); For(i, 1, n) a[i] = read();
    q = read(); For(i, 1, q) Q[i].l = read(), Q[i].r = read(), Q[i].id = i;

    sort(Q + 1, Q + 1 + q);

    Build(1, 1, n);

    For(nr, 1, n) {

        mn[1] -= 1, tag[1] -= 1;

        for (; t1 && a[s1[t1]] < a[nr]; -- t1)
            update(1, 1, n, s1[t1 - 1] + 1, s1[t1], a[nr] - a[s1[t1]]);
        s1[++ t1] = nr;
        
        for (; t2 && a[s2[t2]] > a[nr]; -- t2)
            update(1, 1, n, s2[t2 - 1] + 1, s2[t2], a[s2[t2]] - a[nr]);
        s2[++ t2] = nr;

        sum[1] += t[1], tg[1] += 1;

        for (; tt <= q && Q[tt].r == nr; ++ tt)
            ans[Q[tt].id] = query(1, 1, n, Q[tt].l, nr);
    }

    For(i, 1, q) printf("%lld\n", ans[i]);

    return 0;
}

【CF997E】Good Subsegments (线段树+单调栈)

标签:好的   define   type   ==   个数   com   长度   turn   href   

原文地址:https://www.cnblogs.com/LSTete/p/9757600.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!