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 建樹函式 和普通線段...