1. 程式人生 > 實用技巧 >CF526F Pudding Monsters 線段樹+單調棧

CF526F Pudding Monsters 線段樹+單調棧

剛開始想出了一個分治做法,但是比較麻煩,需要分 4 中情況討論.

後來偷看了一眼標籤發現是線段樹,然後就想出了這個線段樹做法.

考慮序列以 $r$ 為右端點的答案,有 $\sum_{l=1}^{i} max(l,i)-min(l,i)=i-l$.

其中這個條件可以寫成 $max(l,i)-min(l,i)+l=i$.

然後特別注意任何時候都滿足不等式:$r-l \leqslant max(l,r)-min(l,r)$

考慮通過單調棧維護最大/最小值的過程中用線段樹維護以 $i$ 為右端點,$j$ 為左端點的答案.

1. 假如新元素 $i$,則 $i$ 的貢獻就是 $a[i]-a[i]+i=i$.

2. 維護最大值的單調棧要彈棧,則說明一段連續區間的最大值由原來的 $max.top() \Rightarrow a[i]$,則做區間加法.

3. 維護最小值時和最大值同理.

這樣我們只需用線段樹做區間加法就可以求出最小值個數,然後根據不等式:$r-l \leqslant max(l,r)-min(l,r)$ 可知最小值一定為 $i$,加上個數即可.

code:

#include <stack>
#include <cstdio> 
#include <cstring>  
#include <algorithm>   
#define N 300009 
#define ll long long        
#define lson now<<1  
#define rson now<<1|1  
#define inf 1000000000 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int n,a[N];    
stack<int>mi,ma;       
struct data { 
    int x,y;     
    data(int o=inf,int z=0) { x=o,y=z; }          
    data operator+(const data b) const {  
        data c;        
        c.x=min(x,b.x);                
        if(x==c.x) c.y+=y;  
        if(b.x==c.x) c.y+=b.y;   
        return c;   
    }
}s[N<<2];                
int lazy[N<<2];  
void mark(int now,int v) {  
    lazy[now]+=v;   
    s[now].x+=v;    
}
void pushdown(int now) {
    if(lazy[now]) {  
        mark(lson,lazy[now]);  
        mark(rson,lazy[now]);   
        lazy[now]=0;  
    }
}
void modify(int l,int r,int now,int p,int v) {  
    if(l==r) {  
        s[now].x=v;   
        s[now].y=1;   
        return; 
    }  
    pushdown(now);    
    int mid=(l+r)>>1;  
    if(p<=mid)  modify(l,mid,lson,p,v);   
    else modify(mid+1,r,rson,p,v);  
    s[now]=s[lson]+s[rson];   
}
void update(int l,int r,int now,int L,int R,int v) {  
    if(l>=L&&r<=R) {  
        mark(now,v);    
        return; 
    }  
    pushdown(now);  
    int mid=(l+r)>>1;  
    if(L<=mid)  update(l,mid,lson,L,R,v); 
    if(R>mid)   update(mid+1,r,rson,L,R,v);  
    s[now]=s[lson]+s[rson];    
}    
data query(int l,int r,int now,int L,int R) {  
    if(l>=L&&r<=R) { 
        return s[now];  
    } 
    pushdown(now);    
    int mid=(l+r)>>1;  
    if(L<=mid&&R>mid) {
        return query(l,mid,lson,L,R)+query(mid+1,r,rson,L,R);   
    }
    else if(L<=mid) return query(l,mid,lson,L,R);  
    else return query(mid+1,r,rson,L,R);   
}       
int pmi[N],pma[N];   
int main() {  
    // setIO("input");      
    scanf("%d",&n);          
    int x,y,z;  
    for(int i=1;i<=n;++i) { 
        scanf("%d%d",&x,&y);          
        a[y]=x;                       
    }
    ll ans=0;    
    for(int i=1;i<=n;++i) {   
        modify(1,n,1,i,i);    
        z=i-1;   
        while(!mi.empty()&&a[i]<a[mi.top()]) {      
            update(1,n,1,pmi[mi.top()],z,a[mi.top()]-a[i]);          
            z=mi.top()-1;    
            mi.pop();     
        }
        z=i-1; 
        while(!ma.empty()&&a[i]>a[ma.top()]) { 
            update(1,n,1,pma[ma.top()],z,a[i]-a[ma.top()]);  
            z=ma.top()-1;   
            ma.pop();  
        }  
        pmi[i]=mi.empty()?1:mi.top()+1;   
        pma[i]=ma.empty()?1:ma.top()+1;   
        mi.push(i);  
        ma.push(i);        
        ans+=s[1].y;  
    }   
    printf("%lld\n",ans);    
    return 0;   
}