Codeforces 997E Good Subsegments (线段树)
題目鏈接
https://codeforces.com/contest/997/problem/E
題解
經典題,鴿了 159 天終于看明白題解了。。
考慮一個區間是連續的等價于這個區間內的 \((\max-\min)-(r-l)=0\),否則該值 \(\gt 0\).
那么我們考慮從小到大枚舉右端點 \(r\),當 \(r\) 變為 \((r+1)\) 時,對于每個 \(l\),上述值的變化形式就是區間加,以當前的 \(r\) 為右端點的好的區間個數就等于 \(0\) 的個數,這個可以通過維護最小值和最小值的個數來實現。直接線段樹維護就可以得到以 \(r\) 為右端點的好的區間個數。
但是我們要求的是一個區間內的好的子區間個數,也就是當 \(r\) 在一個區間中時某個 \(l\) 區間內 \(0\) 的個數之和。于是我們要做的就是在線段樹上維護最小值為 \(0\) 的個數的歷史和。
這個的維護方法就是,考慮再在每個節點維護 \(0\) 的個數的歷史和以及一個 \(tag\) 標記表示當前區間的最小值要被算多少次,然后每次給 \([1,r]\) 這個區間拆成的 \(O(\log)\) 個線段樹區間中 最小值為 \(0\) 的節點的 \(tag\) 增加 \(1\),然后訪問區間時下放標記同時維護歷史和即可。
時間復雜度 \(O(n+q\log n)\).
代碼
#include<bits/stdc++.h> #define llong long long #define mkpr make_pair #define iter iterator #define riter reversed_iterator #define y1 Lorem_ipsum_dolor using namespace std;inline int read() {int x = 0,f = 1; char ch = getchar();for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}return x*f; }const int mxN = 1.2e5; struct SgTNode {int mn,cnt,tag; llong sum,tag2; } sgt[mxN*4+3]; void pushdown(int u) {if(sgt[u].tag){sgt[u<<1].mn += sgt[u].tag; sgt[u<<1].tag += sgt[u].tag;sgt[u<<1|1].mn += sgt[u].tag; sgt[u<<1|1].tag += sgt[u].tag;sgt[u].tag = 0;}if(sgt[u].tag2){if(sgt[u<<1].mn==sgt[u].mn) {sgt[u<<1].sum += sgt[u<<1].cnt*sgt[u].tag2; sgt[u<<1].tag2 += sgt[u].tag2;}if(sgt[u<<1|1].mn==sgt[u].mn) {sgt[u<<1|1].sum += sgt[u<<1|1].cnt*sgt[u].tag2; sgt[u<<1|1].tag2 += sgt[u].tag2;}sgt[u].tag2 = 0ll;} } void pushup(int u) {sgt[u].mn = min(sgt[u<<1].mn,sgt[u<<1|1].mn);sgt[u].cnt = (sgt[u<<1].mn==sgt[u].mn?sgt[u<<1].cnt:0)+(sgt[u<<1|1].mn==sgt[u].mn?sgt[u<<1|1].cnt:0);sgt[u].sum = sgt[u<<1].sum+sgt[u<<1|1].sum; } void build(int u,int le,int ri) {if(le==ri) {sgt[u].cnt = 1; return;}int mid = (le+ri)>>1;build(u<<1,le,mid); build(u<<1|1,mid+1,ri);pushup(u); } void add(int u,int le,int ri,int lb,int rb,int x) { // if(u==1) {printf("add %d %d %d\n",lb,rb,x);}if(le>=lb&&ri<=rb) {sgt[u].mn += x,sgt[u].tag += x; return;}int mid = (le+ri)>>1; pushdown(u);if(lb<=mid) {add(u<<1,le,mid,lb,rb,x);} if(rb>mid) {add(u<<1|1,mid+1,ri,lb,rb,x);}pushup(u); } void modify(int u,int le,int ri,int lb,int rb) {if(le>=lb&&ri<=rb){if(sgt[u].mn==0) {sgt[u].sum += sgt[u].cnt,sgt[u].tag2++;}return;}int mid = (le+ri)>>1; pushdown(u);if(lb<=mid) {modify(u<<1,le,mid,lb,rb);} if(rb>mid) {modify(u<<1|1,mid+1,ri,lb,rb);}pushup(u); } llong query(int u,int le,int ri,int lb,int rb) {if(le>=lb&&ri<=rb) {return sgt[u].sum;}int mid = (le+ri)>>1; pushdown(u); llong ret = 0ll;if(lb<=mid) {ret += query(u<<1,le,mid,lb,rb);} if(rb>mid) {ret += query(u<<1|1,mid+1,ri,lb,rb);}pushup(u); return ret; }struct Query {int l,r,id;bool operator <(const Query &arg) const {return r<arg.r;} } qr[mxN+3]; llong ans[mxN+3]; int stk1[mxN+3],stk2[mxN+3]; int a[mxN+3]; int n,q,tp1,tp2;int main() {n = read();for(int i=1; i<=n; i++) a[i] = read();q = read();for(int i=1; i<=q; i++) scanf("%d%d",&qr[i].l,&qr[i].r),qr[i].id = i;sort(qr+1,qr+q+1);build(1,1,n);for(int i=1,j=1; i<=n; i++){ // printf("i=%d\n",i);while(tp1>0&&a[i]>a[stk1[tp1]]){add(1,1,n,stk1[tp1-1]+1,stk1[tp1],a[i]-a[stk1[tp1]]);tp1--;}stk1[++tp1] = i;while(tp2>0&&a[i]<a[stk2[tp2]]){add(1,1,n,stk2[tp2-1]+1,stk2[tp2],+a[stk2[tp2]]-a[i]);tp2--;}stk2[++tp2] = i;if(i>1) {add(1,1,n,1,i-1,-1);}modify(1,1,n,1,i);while(j<=q&&qr[j].r==i){ans[qr[j].id] = query(1,1,n,qr[j].l,qr[j].r);j++;}}for(int i=1; i<=q; i++) printf("%I64d\n",ans[i]);return 0; }總結
以上是生活随笔為你收集整理的Codeforces 997E Good Subsegments (线段树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Codeforces 1004F Son
- 下一篇: Codeforces 997D Cycl