航電第三場(單峰數列)

zouyua發表於2024-07-27

單峰數列

  • 題意對於一個整數數列,如果其先嚴格遞增,然後在某一點後嚴格遞減,我們稱這個數列為單峰數列(嚴格遞增和嚴格遞減的部分均要是非空)。
    給定長度為 n 的整數數列 \(a_1,a_2,…,a_n\),請你支援 q 次操作:
  1. 1 l r x:將 \(a_l,a_{l+1},…,a_r\) 的每個數加 x。
  2. 2 l r:判斷 \(a_l,a_{l+1},…,a_r\) 的元素是否全都相同。
  3. 3 l r:判斷 \(a_l,a_{l+1},…,a_r\) 是否嚴格升序排序。當 \(l=r\) 時,認為符合嚴格升序排序。
  4. 4 l r:判斷 \(a_l,a_{l+1},…,a_r\) 是否嚴格降序排序。當 \(l=r\) 時,認為符合嚴格降序排序。
  5. 5 l r:判斷 \(a_l,a_{l+1},…,a_r\) 是否為單峰數列。保證 \(r−l+1≥3\)
  • \(n (3≤n≤10^5), a1,a2,…,an (0≤ai≤10^9), q (1≤q≤2×10^5), −10^9≤x≤10^9\)

賽時前面開的比較慢,這題就稍微有點急,沒滑鼠和習慣問題(還是練習太少了,每次寫完線段樹的題目都沒仔細想),導致賽時沒debug出來,但是思路一眼時對的:

對於這種區間資訊合併和整個區間有關的,不是某個值的資訊維護,一般考慮每個區間的 \(pre, suf\), 和一些關鍵性質,區間加法應該學過線段樹的都會,這裡不詳細講了。這題的關鍵性質就是每個區間其他資訊的關係,記相同為same,上升為add, 下降是sub,單峰為mou,合併時這樣維護(記父親區間為c,左右兒子是a, b:


對於 same, 父親如果要是 same,則左右一定也是 same 且相接的地方是相等的,即 c.same = a.same && b.same && a.suf == b.pre


對於 add,父親如果要是 add,則左右一定也是 add 且相接的地方是遞增的,即 c.add = a.add && b.add && a.suf < b.pre


對於 sub,父親如果要是 sub,則左右一定也是 sub 且相接的地方是遞減的,即 c.sub = a.sub && b.sub && a.suf > b.pre


特別的對於 mou,我們定義小於3的區間 mou = 0, 這裡單峰有三種情況
1.左增右減,相接的地方不相等才是單峰

2.左邊單峰,右邊一定是遞減的,否則會有一個新的峰

3.右邊單峰, 左邊一定是上升的,否則會有一個新的峰



剩下的合併就是左右的 pre 和 suf 了,c.pre = a.pre, c.suf = b.suf

Info merge(const Info& a, const Info& b) {
    Info c;

    if(a.same && b.same && a.suf == b.pre) c.same = 1;
    else c.same = 0;


    if(a.add && b.add && a.suf < b.pre) c.add = 1;
    else c.add = 0;

    if(a.sub && b.sub && a.suf > b.pre) c.sub = 1;
    else c.sub = 0;

    c.len = a.len + b.len;

    if(c.len >= 3) {
        if((a.add && b.sub && (a.suf > b.pre || a.suf < b.pre)) ||
		   (a.add && b.mou && a.suf < b.pre) || 
		   (a.mou && b.sub && a.suf > b.pre) ) 
				c.mou = 1;
        if(a.len == 1) c.mou = a.suf < b.pre && (b.mou || b.sub);
        if(b.len == 1) c.mou = a.suf > b.pre && (a.mou || a.add);
    }
    else c.mou = 0;
    c.pre = a.pre, c.suf = b.suf;
    
    return c;
}

注意查詢時的的資訊返回也是一大坑點, 訪問到空區間時的相關引數賦值(死了一個小時多沒想出來,賽後多虧仙佬指點),這裡不要用普通的那樣查詢,按整個區間塊返回合併,題解也是這樣,避免了大量空區間的引數定義,詳情看程式碼。注意操作一有區間操作,別忘了下傳 lazy標記。

#include<bits/stdc++.h>
#define int long long
using namespace std;
using ull = unsigned long long;
using ll = long long;
using PII = pair<ll,ll>;
using PIII = pair<ll, pair<ll,ll>>;
#define endl "\n"
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0)
#define lowbit(x) (x) & (-x)
#define point(x) setiosflags(ios::fixed)<<setprecision(x)
const int N=1e5+10;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
struct Info {//一定要初始化
    int pre, suf;//區間最前面一個,最後一個,
    int len = 1;
    int lazy;
    int add, sub, mou;//是否是遞增,遞減,單峰
    int same;//是否是相同
    Info (int x) {
        pre = suf = x;
        lazy = 0;
        add = sub = same = 1;
        mou = 0; 
    }
    Info () {
        pre = -1e18, suf = -1e18;
        lazy = 0;
        add = sub = same = 1;
        mou = 0; 
    }
};
Info merge(const Info& a, const Info& b) {
    Info c;

    if(a.same && b.same && a.suf == b.pre) c.same = 1;
    else c.same = 0;


    if(a.add && b.add && a.suf < b.pre) c.add = 1;
    else c.add = 0;

    if(a.sub && b.sub && a.suf > b.pre) c.sub = 1;
    else c.sub = 0;

    c.len = a.len + b.len;

    if(c.len >= 3) {
        if((a.add && b.sub && (a.suf > b.pre || a.suf < b.pre)) ||
		   (a.add && b.mou && a.suf < b.pre) || 
		   (a.mou && b.sub && a.suf > b.pre) ) 
				c.mou = 1;
        if(a.len == 1) c.mou = a.suf < b.pre && (b.mou || b.sub);
        if(b.len == 1) c.mou = a.suf > b.pre && (a.mou || a.add);
    }
    else c.mou = 0;
    c.pre = a.pre, c.suf = b.suf;
    
    return c;
}
struct segtree {
    #define ls (u << 1)
    #define rs (u << 1 | 1)
    int n;
    segtree(int n) {init(n);};
    vector<Info> info;
    vector<int> a;
    void init(int n) {
        this->n = n;
        info.resize(n << 2);
        a.resize(n << 1);
    }
    void push_up(int u) {
        info[u] = merge(info[ls], info[rs]);
    }
    void build(int u, int l, int r)
    {
        if(l == r)
        {
            info[u] = Info();//填值
            return ;
        }
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        push_up(u);
    }
    void settag(int u, int k) {//處理資料
        info[u].pre += k;
        info[u].suf += k;
        info[u].lazy += k;
     }
    void push_down(int u)
    {
        if(info[u].lazy)
        {
            settag(ls, info[u].lazy);
            settag(rs, info[u].lazy);
            info[u].lazy = 0;
        }
    }
    void update(int u, int l, int r, int pos, int k) {
        if(l == r) {
            info[u] = Info(k);
            return;
        }
        push_down(u);
        int mid = l + r >> 1;
        if(pos <= mid) update(ls, l, mid, pos, k);
        else update(rs, mid + 1, r, pos, k);
        push_up(u);

    };
    void update(int u, int l, int r, int x, int y, int k) {
        if(x <= l && r <= y) {
            settag(u, k);
            return;
        }
        push_down(u);
        int mid = l + r >> 1;
        if(x <= mid) update(ls, l, mid, x, y, k);
        if(mid < y) update(rs, mid + 1, r, x, y, k);
        push_up(u);
    }
    void update(int pos, int v) {
        update(1, 1, n, pos, v);
    }
    void update(int x, int y, int k) {
        update(1, 1, n, x, y, k);
    }
    Info query(int u, int l, int r, int x, int y) {
        if (x <= l && r <= y) return info[u];
        push_down(u);
        int mid = l + r >> 1;
        if (y <= mid) return query(ls, l, mid, x, y);
        else if (mid < x) return query(rs, mid + 1, r, x, y);
        else return merge(query(ls, l, mid, x, y), query(rs, mid + 1, r, x, y));
    }
    Info query(int l, int r) {
        return query(1, 1, n, l, r);
    }

};
void solve(){
    int n; cin >> n; 
    segtree tr(n);
    for(int i = 1; i <= n; i ++) {
        int x; cin >> x;
        tr.update(i, x);
    }
    int m; cin >> m;
    while(m --) {
        int op, l, r; cin >> op >> l >> r;
        ll x; 
        if(op == 1) {
            cin >> x;
            tr.update(l, r, x);
        } 
        else if(op == 2) {
            auto t = tr.query(l, r);
            cout << t.same << endl;
        } 
        else if(op == 3) {
            auto t = tr.query(l, r);
            cout << t.add << endl;
        } 
        else if(op == 4) {
            auto t = tr.query(l, r);
            cout << t.sub << endl;
        } 
        else if(op == 5) {
            auto t = tr.query(l, r);
            cout << t.mou << endl;
        }
    }
}
signed main()
{
    IOS;
    int T = 1;
    //cin>>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

相關文章