1. 程式人生 > >【bzoj3064】Tyvj 1518 CPU監控 線段樹維護歷史最值

【bzoj3064】Tyvj 1518 CPU監控 線段樹維護歷史最值

bzoj3064 size define str highlight 難度 變化 觀察 font

題目描述

給你一個序列,支持4種操作:
1.查詢區間最大值;
2.查詢區間歷史最大值;
3.區間加;
4.區間賦值。

輸入

第一行一個正整數T,表示Bob需要監視CPU的總時間。
然後第二行給出T個數表示在你的監視程序執行之前,Bob幹的事讓CPU在這段時間內每個時刻的使用率達已經達到了多少。
第三行給出一個數E,表示Bob需要做的事和詢問的總數。
接下來E行每行表示給出一個詢問或者列出一條事件:
Q X Y:詢問從X到Y這段時間內CPU最高使用率
A X Y:詢問從X到Y這段時間內之前列出的事件使CPU達到過的最高使用率
P X Y Z:列出一個事件這個事件使得從X到Y這段時間內CPU使用率增加Z

C X Y Z:列出一個事件這個事件使得從X到Y這段時間內CPU使用率變為Z
時間的單位為秒,使用率沒有單位。
X和Y均為正整數(X<=Y),Z為一個整數。
從X到Y這段時間包含第X秒和第Y秒。
保證必要運算在有符號32位整數以內。

輸出

對於每個詢問,輸出一行一個整數回答。

樣例輸入

10
-62 -83 -9 -70 79 -78 -31 40 -18 -5
20
A 2 7
A 4 4
Q 4 4
P 2 2 -74
P 7 9 -71
P 7 10 -8
A 10 10
A 5 9
C 1 8 10
Q 6 6
Q 8 10
A 1 7
P 9 9 96

A 5 5
P 8 10 -53
P 6 6 5
A 10 10
A 4 4
Q 1 5
P 4 9 -69

樣例輸出

79
-70
-70
-5
79
10
10
79
79
-5
10
10


題解

線段樹維護歷史最值

先粘一發吉老師題解:

剛接觸這一類問題時,這個例題的難度可能較高,所以我們先忽略區間賦值操作。
考慮使用傳統的懶標記來解決,首先如果只是詢問區間最大值,只面要使用區間加減這一個懶標記(用 $Add$ 表示)就能解決。
現在考慮詢問區間歷史最大值的最大值。我們定義一種新的懶標記:歷史最大的加減標記(用 $Pre$ 表示)。這個標記的定義是:從上一次把這個節點的標記下傳的時刻到當前時刻這一時間段中,這個節點中的 $Add$ 標記值到達過的最大值。

現在考慮把第 $i$ 個節點的標記下傳到它的兒子 $l$ ,不難發現標記是可以合並的:$Pre_l = \text{max}(Pre_l, Add_l + Pre_i), Add_l = Add_l + Add_i$ 。至於區間歷史最大值信息的更新也與標記的合並類似,只面要將當前的區間最大值加上 $Pre_i$ 然後與原來的歷史最大值進行比較即可。
現在回到原題,我們觀察在修改操作過程中,被影響到的節點的變化:如果一個節點沒有發生標記下傳,那麽最開始它一直被區間加減操作所影響,這時我們可以用上面描述的Pre標記來記錄,直到某一時刻,這個節點被區間覆蓋標記影響A,那麽這時這個節點中的所有數都變得完全相同,再之後的所有區間加減修改,對這個節點來說,與區間覆蓋操作並沒有不同。
因此每一個節點受到的標記可以分成兩個部分:第一個部分是區間加減,第二個部分是區間覆蓋。因此我們可以用 $(x, y)$ 來表示歷史最值標記,它的定義是當前區間在第一階段時最大的加減標記是 $x$ ,在第二個階段時最大的覆蓋標記是 $y$ 。顯然這個標記是可以進行合並與更新的。
到此我們就使用最傳統的懶標記方法解決了這個問題,時間復雜度 $O(m \log n)$ 。

然而我的做法可能和吉老師這道題的做法不太一樣。。。用的是吉老師下一道題的做法。。。

首先需要弄明白吉老師講的只有區間加減、求區間歷史最值的做法(上面紅色部分)。

定義標記 $(a,b)$ 表示先給區間內所有數加上 $a$ ,再與 $b$ 取 $\text{max}$ ,即 $A_i=\text{max}(A_i+a,b)$ 。

那麽區間加操作就相當於打標記 $(z,-\infty )$ ,區間賦值操作就相當於打標記 $(-\infty ,z)$ 。

考慮兩個標記合並:將標記 $(c,d)$ 合並到 $(a,b)$ 上(即先有 $(a,b)$ 後有 $(c,d)$),可以得到結果 $(a+c,\text{max}(b+c,d))$ 。

考慮兩個標記取最大值:將標記作用後的值看作原值的函數,那麽顯然是一個分段函數,第一段是 $y=b$ ,第二段是 $y=x+a$ 。那麽標記取最大值就是取兩個函數最上方的部分,顯然是兩段分別取最大值,即 $(a,b)$ 與 $(c,d)$ 取最大值後為 $(\text{max}(a,c),\text{max}(b,d))$ 。

至此已經完成了吉老師所講的標記的 “+” 和 “max” ,可以實現所有操作。

時間復雜度 $O(m\log n)$ 。

具體還是見代碼吧。代碼中p(pre)開頭的指的是歷史,n(now)開頭的指的是當前。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100010
#define inf 0x3f3f3f3f
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
struct data
{
	int x , y;
	data(int a = 0 , int b = -inf) {x = a , y = b;}
	data operator+(const data &a)const {return data(max(-inf , x + a.x) , max(y + a.x , a.y));}
	data operator*(const data &a)const {return data(max(x , a.x) , max(y , a.y));}
}ntag[N << 2] , ptag[N << 2];
int nmax[N << 2] , pmax[N << 2];
char str[5];
inline void pushup(int x)
{
	nmax[x] = max(nmax[x << 1] , nmax[x << 1 | 1]);
	pmax[x] = max(pmax[x << 1] , pmax[x << 1 | 1]);
}
inline void pushdown(int x)
{
	int l = x << 1 , r = x << 1 | 1;
	ptag[l] = ptag[l] * (ntag[l] + ptag[x]);
	ntag[l] = ntag[l] + ntag[x];
	pmax[l] = max(pmax[l] , max(nmax[l] + ptag[x].x , ptag[x].y));
	nmax[l] = max(nmax[l] + ntag[x].x , ntag[x].y);
	ptag[r] = ptag[r] * (ntag[r] + ptag[x]);
	ntag[r] = ntag[r] + ntag[x];
	pmax[r] = max(pmax[r] , max(nmax[r] + ptag[x].x , ptag[x].y));
	nmax[r] = max(nmax[r] + ntag[x].x , ntag[x].y);
	ptag[x] = ntag[x] = data();
}
void build(int l , int r , int x)
{
	ntag[x] = ptag[x] = data();
	if(l == r)
	{
		scanf("%d" , &nmax[x]) , pmax[x] = nmax[x];
		return;
	}
	int mid = (l + r) >> 1;
	build(lson) , build(rson);
	pushup(x);
}
void update(int b , int e , data a , int l , int r , int x)
{
	if(b <= l && r <= e)
	{
		ptag[x] = ptag[x] * (ntag[x] + a);
		ntag[x] = ntag[x] + a;
		pmax[x] = max(pmax[x] , max(nmax[x] + a.x , a.y));
		nmax[x] = max(nmax[x] + a.x , a.y);
		return;
	}
	pushdown(x);
	int mid = (l + r) >> 1;
	if(b <= mid) update(b , e , a , lson);
	if(e > mid) update(b , e , a , rson);
	pushup(x);
}
int query(int b , int e , bool flag , int l , int r , int x)
{
	if(b <= l && r <= e) return flag ? pmax[x] : nmax[x];
	pushdown(x);
	int mid = (l + r) >> 1 , ans = -inf;
	if(b <= mid) ans = max(ans , query(b , e , flag , lson));
	if(e > mid) ans = max(ans , query(b , e , flag , rson));
	return ans;
}
int main()
{
	int n , m , x , y , z;
	scanf("%d" , &n);
	build(1 , n , 1);
	scanf("%d" , &m);
	while(m -- )
	{
		scanf("%s%d%d" , str , &x , &y);
		if(str[0] == ‘Q‘) printf("%d\n" , query(x , y , 0 , 1 , n , 1));
		if(str[0] == ‘A‘) printf("%d\n" , query(x , y , 1 , 1 , n , 1));
		if(str[0] == ‘P‘) scanf("%d" , &z) , update(x , y , data(z , -inf) , 1 , n , 1);
		if(str[0] == ‘C‘) scanf("%d" , &z) , update(x , y , data(-inf , z) , 1 , n , 1);
	}
	return 0;
}

【bzoj3064】Tyvj 1518 CPU監控 線段樹維護歷史最值