線段樹-Pudding Monster CF526F-單調棧

Pudding Monster

題目連接:https://www.luogu.org/problem/show?pid=CF526F

問題提出

  • 給長度爲nn的排列AA.
  • 問有多少(l,r)(l,r),使得將Al,Al+1,...,ArA_l,A_{l+1},...,A_r排序之後是連續的一段數.
  • n105n \le 10^5

問題解決

判斷一段區間是否能連續,只要判斷是否滿足maxmin=rlmax-min=r-l即可.

也即判斷區間是否滿足maxmin+l=rmax-min+l = r.

先天有maxmin+lrmax-min+l \ge r

因此我們可以考慮枚舉區間端點rr,然後使用線段樹維護左邊所有區間(每個區間用左端點唯一標識)的l+maxminl+max-min的值.

我們還需要線段樹滿足求線段內最小值是誰,最小值有多少個的功能.

考慮到區間右端點向右移動的過程中,原有的區間再加上rr形成的新區間的maxmax不會變小,minmin值不會變大.

maxmax變大也會是一批區間一起增大,增量也是相同的,因此可以轉化爲區間修改操作.

我們可以用22個單調棧來維護當前的最大值序列,和最小值序列.

每次將rr點加入左邊所有的區間,從而形成新的區間,根據單調棧保存的信息,確定出哪些區間的最大值或者是最小值需要被修改,然後進行區間修改.

詢問時候,直接查詢[1,r][1,r]內最小值爲00的個數,算貢獻.

代碼

#include <iostream>
#include <algorithm>
#include <cstring>
#define pr(x) std::cout << #x << ':' << x << std::endl
#define rep(i,a,b) for(int i = a;i <= b;++i)
#define clr(x) memset(x,0,sizeof(x))
#define setinf(x) memset(x,0x3f,sizeof(x))

const int N = 300007,inf = 1e9;
int n,m;
typedef std::pair<int,int> pii;
int a[N];

struct node{
    int val,cnt,tag,len;
    node(int val = 0,int cnt = 1,int tag = 0,int len = 1):val(val),cnt(cnt),tag(tag),len(len){}
}ns[N<<2];
//線段樹維護的是區間最小值以及最小值的個數
inline void pushdown(int o) {
    if(ns[o].tag) {
        ns[o<<1].val += ns[o].tag;
        ns[o<<1|1].val += ns[o].tag;
        ns[o<<1].tag += ns[o].tag;
       	ns[o<<1|1].tag += ns[o].tag;
        ns[o].tag = 0;
    }
}

inline node maintain(node &lch,node &rch) {
    node res = node(std::min(lch.val,rch.val),0,0,lch.len+rch.len);
    if(lch.val == res.val) res.cnt += lch.cnt;
    if(rch.val == res.val) res.cnt += rch.cnt;
    return res;
}

inline void change(int o,int l,int r,int cl,int cr,int val) {
    if(cl <= l && r <= cr) {
        ns[o].tag += val;
        ns[o].val += val;
        return ;
    }	
    if(r < cl || cr < l) return;
    int mid = (l + r) >> 1;
    pushdown(o);
    change(o<<1,l,mid,cl,cr,val);
    change(o<<1|1,mid+1,r,cl,cr,val);
    ns[o] = maintain(ns[o<<1],ns[o<<1|1]);
}

inline node query(int o,int l,int r,int ql,int qr) {
    if(ql <= l && r <= qr) 
        return ns[o];
    if(r < ql || qr < l)
        return node(inf,1,0,0);
    int mid = (l + r) >> 1;
    pushdown(o);
    node lch = query(o<<1,l,mid,ql,qr);
    node rch = query(o<<1|1,mid+1,r,ql,qr);
    return maintain(lch,rch);
}

inline void build(int o,int l,int r) {
    if(l == r) {
        ns[o] = node(0,1,0,1);
        return ;
    }
    int mid = (l + r) >> 1;
    build(o<<1,l,mid);
    build(o<<1|1,mid+1,r);
    ns[o] = maintain(ns[o<<1],ns[o<<1|1]);
}

pii mx[N],mi[N];
int topx,topi;

int main() {
    std::cin >> n;
    build(1,1,n);
    rep(i,1,n) {
        int idx,y;
        std::cin >> idx >> y;
        a[idx] = y;
    }
    long long ans = 0;
    rep(i,1,n) {
        change(1,1,n,i,i,i);
        while(topx > 0 && mx[topx].first < a[i]) {
            change(1,1,n,mx[topx-1].second+1,mx[topx].second,a[i] - mx[topx].first);
            topx--;
        }
        mx[++topx] = (pii){a[i],i};
        while(topi > 0 && mi[topi].first > a[i]) {
            change(1,1,n,mi[topi-1].second+1,mi[topi].second,mi[topi].first - a[i]);
            topi--;
        }
        mi[++topi] = (pii){a[i],i};
        
        change(1,1,n,1,i,-i);
        node res = query(1,1,n,1,i);
        ans += res.cnt;
        change(1,1,n,1,i,i);
    }
    std::cout << ans << std::endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章