Codeforces 997E Good Subsegments (线段树)
題目鏈接
https://codeforces.com/contest/997/problem/E
題解
經(jīng)典題,鴿了 159 天終于看明白題解了。。
考慮一個(gè)區(qū)間是連續(xù)的等價(jià)于這個(gè)區(qū)間內(nèi)的 \((\max-\min)-(r-l)=0\),否則該值 \(\gt 0\).
那么我們考慮從小到大枚舉右端點(diǎn) \(r\),當(dāng) \(r\) 變?yōu)?\((r+1)\) 時(shí),對于每個(gè) \(l\),上述值的變化形式就是區(qū)間加,以當(dāng)前的 \(r\) 為右端點(diǎn)的好的區(qū)間個(gè)數(shù)就等于 \(0\) 的個(gè)數(shù),這個(gè)可以通過維護(hù)最小值和最小值的個(gè)數(shù)來實(shí)現(xiàn)。直接線段樹維護(hù)就可以得到以 \(r\) 為右端點(diǎn)的好的區(qū)間個(gè)數(shù)。
但是我們要求的是一個(gè)區(qū)間內(nèi)的好的子區(qū)間個(gè)數(shù),也就是當(dāng) \(r\) 在一個(gè)區(qū)間中時(shí)某個(gè) \(l\) 區(qū)間內(nèi) \(0\) 的個(gè)數(shù)之和。于是我們要做的就是在線段樹上維護(hù)最小值為 \(0\) 的個(gè)數(shù)的歷史和。
這個(gè)的維護(hù)方法就是,考慮再在每個(gè)節(jié)點(diǎn)維護(hù) \(0\) 的個(gè)數(shù)的歷史和以及一個(gè) \(tag\) 標(biāo)記表示當(dāng)前區(qū)間的最小值要被算多少次,然后每次給 \([1,r]\) 這個(gè)區(qū)間拆成的 \(O(\log)\) 個(gè)線段樹區(qū)間中 最小值為 \(0\) 的節(jié)點(diǎn)的 \(tag\) 增加 \(1\),然后訪問區(qū)間時(shí)下放標(biāo)記同時(shí)維護(hù)歷史和即可。
時(shí)間復(fù)雜度 \(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; }總結(jié)
以上是生活随笔為你收集整理的Codeforces 997E Good Subsegments (线段树)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Codeforces 1004F Son
- 下一篇: Codeforces 997D Cycl