[CF997E] Good Subsegments 题解

Description

有一个 \(1-n\) 的排列 \(P\) \((1\le n\le 1.2*10^5)\)

如果区间 \([l,r]\) 中的数在排序后是连续的,那么我们称它为好区间。

例如,\([1, 3, 2, 5, 4]\)中的好区间有:

\([1,1], [1, 3], [1, 5], [2, 2], [2, 3], [2, 5], [3, 3], [4, 4], [4, 5], [5, 5].\)

\(q\) 次询问,每次问 \([l,r]\) 内,有多少子区间是好的?

\(n,q\le 1.2\times 10^5\)

Sol

考虑好区间的特点,假设区间 \([l,r]\) 为好区间,那么它一定满足 \(\max_{l}^r a_i - \min_{l}^r a_i = r - l\)

考虑枚举右端点,然后考虑每个后缀 \([L,R],[L+1,R],...,[R,R]\) 的贡献,我们维护每个左端点的 \(f(x)=max - min + l - r\),观察有多少点的值为 \(0\)

观察到 \(f(R)=0\)\(0\) 为所有 \(f\) 中的最小值,那么我们要维护的就是最小值个数,那么我们可以采用线段树来维护。

我们把每个询问按照 \(r\) 排序,考虑询问移动产生的贡献:

\(l\) 变化:我们提前把每个点的权值设为 \(l\) ,这样我们就不用考虑 \(l\) 的贡献了。

\(r\) 变化:因为我们固定了 \(r\) 端点,所以每次移动时区间减 \(1\) 即可。

\(max,min\) 变化:我们用单调栈来维护每个后缀需要用到的最大与最小值,在线段树中更改即可。

由于我们需要得到每一次 \(R\) 变化的值,故线段树实现时还要记忆每次的 \(ans\)

具体细节可以看代码。

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
int Read() {
    int x = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)) {if(ch == '-')  f = -1; ch = getchar();}
    while(isdigit(ch)) {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
    return x * f;
}
int n, a[200005], q, Ans[200005], minn[800005], mcnt[800005], ans[800005], addv[800005], tim[800005];
struct node {
    int l, r, id;
    bool operator < (node A) const {return r < A.r;}
}que[200005];
int stk1[200005], stk2[200005], tp1, tp2;
void pushup(int o) {
    minn[o] = min(minn[o << 1], minn[o << 1 | 1]);
    mcnt[o] = 0;
    mcnt[o] += (minn[o << 1] == minn[o]) * mcnt[o << 1];
    mcnt[o] += (minn[o << 1 | 1] == minn[o]) * mcnt[o << 1 | 1];
    ans[o] = ans[o << 1] + ans[o << 1 | 1];
}
void build(int o, int l, int r) {
    if(l == r) {minn[o] = l, mcnt[o] = 1; return ;}
    int mid = (l + r) >> 1;
    build(o << 1, l, mid); build(o << 1 | 1, mid + 1, r);
    pushup(o);
}
void pusha(int o, int x) {minn[o] += x; addv[o] += x;}
void pusht(int o, int x) {ans[o] += mcnt[o] * x; tim[o] += x;}
void pushdown(int o) {
    if(addv[o])  pusha(o << 1, addv[o]), pusha(o << 1 | 1, addv[o]), addv[o] = 0;
    if(tim[o]) {
        if(minn[o << 1] == minn[o])  pusht(o << 1, tim[o]);
        if(minn[o << 1 | 1] == minn[o])  pusht(o << 1 | 1, tim[o]);
        tim[o] = 0;
    }
}
void modify(int o, int l, int r, int nl, int nr, int val) {
    if(nl <= l && r <= nr)  return pusha(o, val);
    int mid = (l + r) >> 1; pushdown(o);
    if(nl <= mid)  modify(o << 1, l, mid, nl, nr, val);
    if(mid < nr)  modify(o << 1 | 1, mid + 1, r, nl, nr, val);
    pushup(o);
}
int query(int o, int l, int r, int nl, int nr) {
    if(nl <= l && r <= nr)  return ans[o];
    int mid = (l + r) >> 1, res = 0; pushdown(o);
    if(nl <= mid)  res += query(o << 1, l, mid, nl, nr);
    if(mid < nr)  res += query(o << 1 | 1, mid + 1, r, nl, nr);
    return res;
}
signed main() {
    n = Read();
    for(int i = 1; i <= n; i++)  a[i] = Read();
    build(1, 1, n);
    q = Read();
    for(int i = 1; i <= q; i++)  que[i].l = Read(), que[i].r = Read(), que[i].id = i;
    sort(que + 1, que + q + 1);
    int cnt = 1;
    for(int i = 1; i <= n; i++) {
        pusha(1, -1);
        while(tp1 && a[i] > a[stk1[tp1]]) {
            modify(1, 1, n, stk1[tp1 - 1] + 1, stk1[tp1], a[i] - a[stk1[tp1]]);
            --tp1;
        }
        while(tp2 && a[i] < a[stk2[tp2]]) {
            modify(1, 1, n, stk2[tp2 - 1] + 1, stk2[tp2], a[stk2[tp2]] - a[i]);
            --tp2;
        }
        stk1[++tp1] = i; stk2[++tp2] = i;
        pusht(1, 1);
        while(que[cnt].r == i && cnt <= q)  Ans[que[cnt].id] = query(1, 1, n, que[cnt].l ,que[cnt].r), ++cnt;
    }
    for(int i = 1; i <= q; i++)  printf("%lld\n", Ans[i]);
    return 0;
}
posted @ 2020-11-04 18:48  verjun  阅读(141)  评论(0编辑  收藏  举报