[CF997E] Good Subsegments 題解
阿新 • • 發佈:2020-11-04
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]\)
考慮列舉右端點,然後考慮每個字尾 \([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; }