1. 程式人生 > 實用技巧 >P4314 CPU監控

P4314 CPU監控

線段樹標記好題

這題要支援區間加,區間賦值,區間查最大值,區間查歷史最大值。

直接用線段樹維護區間最大值和歷史最大值,再打倆 \(tag\) 是不夠的,因為\(tag\) 不一定能夠及時下傳至葉子節點

因此我們需要額外多打倆 \(tag\):(從上次更新以後)加法標記的歷史最大值,(從上次更新以後)賦值標記的歷史最大值。並且我們保證賦值操作絕對優於加法操作,即原有加法標記,再賦值以後會取消加法標記(但是不會清空加法標記的歷史最大值);原有歷史標記,再加法就直接視為賦值為某個值的操作。

因此,我們在操作標記的時候要對有賦值標記的情況進行特殊處理。

需要注意的是,下放標記的時候,我們需要先下放加法標記,再下放賦值標記

,因為加法標記的歷史最大值記錄的是上次更新之後到第一次賦值標記之前的所有加法的最大值,我們將用 \(now~max + history~add~tag\) 來更新 \(history~max\),而這個 \(now~max\) 應該是上一次更新之後的 \(now~max\),如果先用賦值標記更新的話,\(now~max\) 資訊就會出錯。

賦值標記不需要管 \(now~max\) 是否正確,因為用不到。

關鍵程式碼:

//vst : 是否存在賦值標記
inline void push_set(int cur, int v, int hv) {
	if (!cur)	return ;
	mx[cur] = v, MAX(hmx[cur], hv);
	if (vst[cur])
		MAX(hstag[cur], hv), stag[cur] = v;//Attention!!
	else	vst[cur] = true, stag[cur] = v, hstag[cur] = hv;
	atag[cur] = 0;
}
inline void push_add(int cur, int v, int hv) {
	if (!cur)	return ;
	MAX(hmx[cur], mx[cur] + hv), mx[cur] += v;//Attention!!!  <-error
	if (vst[cur])	push_set(cur, stag[cur] + v, stag[cur] + hv);
	else	MAX(hatag[cur], atag[cur] + hv), atag[cur] += v;
}
inline void pushdown(int cur) {
	if (vst[cur] && atag[cur])	exit(-1);
	push_add(ls[cur], atag[cur], hatag[cur]),
	push_add(rs[cur], atag[cur], hatag[cur]),
	atag[cur] = hatag[cur] = 0;
	if (vst[cur]) {
		push_set(ls[cur], stag[cur], hstag[cur]),
		push_set(rs[cur], stag[cur], hstag[cur]),
		stag[cur] = hstag[cur] = 0;
		vst[cur] = 0;
	}
}