區間歷史最值筆記 線段樹

2022-05-04 13:45:10 字數 3352 閱讀 4841

cpu監控

要你維護對序列上的操作:

1、區間加

2、區間賦值

3、區間最大值

4、區間歷史最值

使用線段樹+標記維護,記錄節點上發生的所有事件。

注意到乙個線段樹節點,如果進行了modify操作,那麼接下來的加法都可以認為是modify。

那麼乙個節點上的標記長度就至多為2了。

設 \(\text\) 標記時節點實際要加的值,\(\text\) 表示覆蓋。

考慮記錄 \(\text\) 標記為所有祖先的 \(\text\) 標記,歷史上能達到的最大值。\(\text\) 同理。

下放標記時注意到子節點上的標記發生時間在該點之前,依此時間順序進行合併如下:

當下放加法標記,一方面由於 \(\text\) 是上方加法操作中最大增加的值,用它結合實際值來更新歷史最值

若子節點無 \(\text\) 標記那麼標記向 \(\text\) 上打,否則向 \(\text\) 上打。

並且要結合該子節點當前的操作值,更新對應操作的歷史最值

當下放覆蓋標記,先用 \(\text\) (歷史最大覆蓋值)更新子節點的歷史最值和 \(\text\)。

然後用實際覆蓋值修改實際最大值和實際覆蓋值。

下放標記要注意順序,加法在先,賦值在後。由於該寫法下放標記的運算元量很小,可能存在速度優勢。

下面是這個題的**,它在2023年1月19日是洛谷的rank1.

#include#includeusing namespace std;

const int n = 100005;

const int inf = 1e9;

struct io_tp

inline char readc()

}io;

inline void ckmax(int&x,const int y)

struct segtree

}t[n << 2];

#define lc (o << 1)

#define rc (o << 1 | 1)

void pushup(int o)

void dadd(int o, int x, int y)

void dmod(int o, int x, int y)

void pushdown(int o)

if(t[o].mod != -inf)

}void build(int o, int l, int r)

int mid = (l + r) >> 1;

build(lc, l, mid); build(rc, mid + 1, r);

pushup(o); }

void update(int o, int l, int r, int pl, int pr, int x)

pushdown(o);

int mid = (l + r) >> 1;

update(lc, l, mid, pl, pr, x);

update(rc, mid + 1, r, pl, pr, x);

pushup(o); }

void modify(int o, int l, int r, int pl, int pr, int x)

pushdown(o);

int mid = (l + r) >> 1;

modify(lc, l, mid, pl, pr, x);

modify(rc, mid + 1, r, pl, pr, x);

pushup(o); }

int query(int o, int l, int r, int pl, int pr)

int query(int o, int l, int r, int pl, int pr)

}z[1];

int main()

return 0;

}

上面的做法有侷限性。

設分段函式 \(f(x)=max(x+a,b)\),則區間修改操作均可以寫成對乙個區間作用上乙個函式。

並且 \(h(x)=max(f(x),g(x))\) 也依然是這樣的函式。

那麼我們可以維護區間的分段函式 \(f(x)\) 和當前所有函式「最高輪廓」 \(g(x)\)。

考慮下放標記,\(f(x)\) 直接合併即可,考慮 \(g(x)\);

相當於一堆函式作用到乙個函式 \(f'\) 上,最高的輪廓應當是這堆函式的 \(g\) 作用於 \(f\),然後再把這個和之前的 \(g'\) 取較高的。

若要維護區間歷史最值,下放標記時用 \(g(\text)\) 更新 \(\text\);再令 \(\text=f(\text)\)。

要注意你這個操作是對於這個區間整體而言的,就是說該區間的歷史最值肯定要找最靠右的位置,所以要用當時的 \(\text\) 更新。

下面是清華集訓那題的**,只有單點查詢(區間歷史最值只要在 pushdown 裡加兩句話)

#include#includeusing namespace std;

typedef long long ll;

const int n = 500004;

const ll inf = 1e16;

inline void ckmax(ll&x, ll y)

struct io_tp

inline char readc()

}io;

int n, m, a[n];

struct node

node operator + (const node& t)

ll operator () (ll x)

void operator |= (const node& t)

} null;

struct segtree

void pushdown(int o)

void modify(int o, int l, int r, int pl, int pr, const node& z)

pushdown(o);

int mid = (l + r) >> 1;

modify(lc, l, mid, pl, pr, z);

modify(rc, mid + 1, r, pl, pr, z); }

ll query(int o, int l, int r, int p)

ll query(int o, int l, int r, int p)

}z[1];

int main()

else

}return 0;

}

線段樹,區間最值

codeforces 91b queue 線段樹,區間最值 題意是,對於給定區間內的每個元素,要求求出離他最遠的那個元素之間的距離。可以維護乙個線段樹的最小值,每次對於乙個元素,查詢其最右邊的元素的位置。include include include includeusing namespace s...

區間最值與線段樹

區間最值問題 有如下無序序列,求任意子區間段的最大值。接著,我們要用分治的思想來快速地解決上面的問題。在解決問題之前,先介紹一些分治的概念。二分查詢 二分查詢是分治思想的典型運用 我們有如下序列 a1,a2,a3 an.要查詢其中等於b的元素。一種方法就是乙個個對比,看看是不是相等,時間複雜度為n。...

動態區間最值(RMQ) 線段樹

建樹 a aa陣列為初始陣列,tre etree tree 陣列為樹 typedef long long ll const int inf 0x7fffffff const int maxn 2e5 10 int a maxn int tree maxn 2 lz maxn 2 建樹函式 和普通線段...