1. 程式人生 > 其它 >cf739 C. Alyona and towers(線段樹)

cf739 C. Alyona and towers(線段樹)

題意:

給定陣列,要求實現兩種操作:區間加、查詢陣列中最長的 “先嚴格上升再嚴格下降” 的連續子區間的長度。

注意只嚴格上升或只嚴格下降或只有一個元素也是合法的。

思路:

把長為n的原陣列處理成相鄰項的差,長為n-1。

考慮相鄰項的差的符號,1,0,-1表示大於零,等於零,小於零。滿足條件的形式為 \(+1,+1,+1,-1,-1\) ,即差的符號函式值非嚴格遞減,不能出現0。

用線段樹維護差的符號函式值,記錄區間最大的連續非嚴格遞減子列的長度、從左端點開始的長度和從右端點開始的長度。合併區間時要判斷左右兒子相接的地方能不能合併。

因為差分了所以只需單點修改。查詢時直接輸出根節點上的答案。另外再實現個建樹函式和pushup即可,不需要pushdown。pushup和main函式中有挺多細節,其他幾個函式跟普通線段樹差不多。

對於每次修改,首先直接修改差分陣列(即原陣列),如果改變了差的符號才需要改線段樹。

n=1時根本沒有相鄰項,特判一下。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 3e5 + 5;
ll w[N];
int sgn(ll x) { //返回x的符號
    if(x > 0) return 1; if(x < 0) return -1;
    return 0;
}

struct node {
    int l, r, len, llen, rlen;
} tr[N*4];
void pushup(int u)
{
    tr[u].len = max(tr[u<<1].len, tr[u<<1|1].len);
    tr[u].llen = tr[u<<1].llen, tr[u].rlen = tr[u<<1|1].rlen;
    if(w[tr[u<<1].r] && w[tr[u<<1|1].l] &&
        sgn(w[tr[u<<1].r]) >= sgn(w[tr[u<<1|1].l]))
    {
        tr[u].len = max(tr[u].len, tr[u<<1].rlen + tr[u<<1|1].llen);
        if(tr[u<<1].llen == tr[u<<1].r - tr[u<<1].l + 1)
            tr[u].llen += tr[u<<1|1].llen;
        if(tr[u<<1|1].rlen == tr[u<<1|1].r - tr[u<<1|1].l + 1)
            tr[u].rlen += tr[u<<1].rlen;
    }
}
void build(int u, int l, int r)
{
    if(l == r) //只維護符號
    {
        bool tmp = w[l];
        tr[u] = {l, r, tmp, tmp, tmp};
    }
    else
    {
        tr[u] = {l, r};
        int mid = l + r >> 1;
        build(u<<1, l, mid), build(u<<1|1, mid+1, r);
        pushup(u);
    }
}
void modify(int u, int p, int x) //把單點p改為x
{
    if(tr[u].l == tr[u].r)
    {
        bool tmp = x;
        tr[u] = {tr[u].l, tr[u].r, tmp, tmp, tmp};
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if(p <= mid) modify(u<<1, p, x);
    else modify(u<<1|1, p, x);
    pushup(u);
}

signed main()
{
    int n, q; scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%lld", &w[i]);

    if(n == 1) {
        scanf("%d", &q); while(q--) puts("1");
        return 0;
    }

    for(int i = 1; i < n; i++) w[i] = w[i+1] - w[i]; //差分
    build(1, 1, n-1); //維護n-1個相鄰差

    scanf("%d", &q); while(q--)
    {
        int l, r, d; scanf("%d%d%d", &l, &r, &d);

        if(l > 1) {
            if(sgn(w[l-1]) != sgn(w[l-1]+d))
                 w[l-1] += d, modify(1, l-1, sgn(w[l-1]));
            else w[l-1] += d;
        }
        if(r < n) {
            if(sgn(w[r]) != sgn(w[r]-d))
                w[r] -= d, modify(1, r, sgn(w[r]));
            else w[r] -= d;
        }

        printf("%d\n", tr[1].len + 1);
    }

    return 0;
}