線段樹 區間修改 最大值

2021-08-01 18:24:48 字數 4181 閱讀 8112

最大值(區間修改)

總時間限制: 10000ms 單個測試點時間限制: 1000ms 記憶體限制: 65536kb

描述 在n(1<=n<=100000)個數a1…an組成的序列上進行m(1<=m<=100000)次操作,操作有兩種:

(1)1 lr c:表示把a[l]到a[r]增加c(c的絕對值不超過10000);

(2)2 lr:詢問a[l]到a[r]之間的最大值。

輸入 第一行輸入n(1<=n<=100000),表示序列的長度,接下來n行輸入原始序列;接下來一行輸入m(1<=m<=100000)表示操作的次數,接下來m行,每行為1 l r c或2 l r

輸出 對於每個操作(2)輸出對應的答案。

樣例輸入

512

3453

2 1 4

1 1 3 3

2 3 5

樣例輸出

4

6

提示

保證序列中的所有的數都在longint範圍

這道題是最基本的區間修改問題,並且只需要求最大值。在開始之前,我們先明確一下需要的資料和對應的含義。

我們需要一顆用於儲存最大值的線段樹tree和懶惰標記樹lazy,lazy[i]的含義是i結點對應的區間中的每個值都加上lazy[i]。注意,假設我們只進行了一次1操作,那麼此時如果lazy[i]不為0,則lazy[i*2]和lazy[i*2+1]必為0;反之,如果lazy[i*2]和lazy[i*2+1]不為0,那麼lazy[i]必為0。因為在同一時刻我們只儲存乙個操作,如果需要我們再分解操作。我們把置lazy[i]為0並更新lazy[i*2]和lazy[i*2+1]等其他資料的操作叫做pushdown,即把操作從乙個結點往下推,往下分解。

這麼看來,tree的含義就是:如果tree[i]已經因pushdown更新,那麼它代表i結點對應的區間中的最大值;如果沒有更新,那就從樹的根部開始一直更新下來。以上的含義還是相對抽象,讓我們從**中分析下。

先找題意宣告一下變數:

const

int maxn=100005;

int tree[maxn*3];

intlazy[maxn*3];

int n,m;

由於線段樹在一開始沒有任何更新操作,根據定義它就代表對應區間的最大值,跟點修改線段樹一樣,沒有特殊含義,所以我們可以遞迴輸入建樹:

void build(int node=1, int l=1, int r=n)

int mid=(l+r)/2;

build(node<<1, l, mid);

build((node<<1)+1, mid+1, r);

tree[node]=std::max(tree[node<<1], tree[(node<<1)+1]);

}

這個**應在輸入n之後,輸入m之前執行,沒什麼好說的。

然後是操作1,我們姑且叫它change函式吧。為了節省一點棧空間,我們把待修改區間和增加的值宣告為全域性變數:

int g_l,g_r,g_add;

void change(int node=1, int l=1, int r=n)

int mid=(l+r)/2;

int lc=node<<1;

int rc=(node<<1)+1;

//現在要更新子結點了對吧,既然子結點的最大值還沒有加上g_add,那我們怎麼知道加了後的值是多少呢?

pushdown(node); //那就更新它,把lazy記號推下去

if(g_l<=mid)

change(lc, l, mid);

if(g_r>mid)

change(rc, mid+1, r);

tree[node]=std::max(tree[lc],tree[rc]); //記住要回來更新父結點

}

既然已多次說道pushdown函式,就讓我們來看看他的**:

void pushdown(int node)

}

注意,pushdown的實質就是把操作1分解,讓子結點儲存分解後的操作,這樣就不必進行所有的操作,因為大部分都是無效的。除了讓子結點的lazy值加上父結點(當前結點)的lazy值,子結點的最大值tree也要加上lazy值。再次強調:我們的tree儲存對應區間的最大值,在正常訪問tree結點時,則它已經加上了該加的值,lazy是儲存子結點還沒有加上的值的。多領會一下。

最後是查詢操作:

//使用g_l和g_r

int query(int node=1, int l=1, int r=n)

int mid=(l+r)/2;

int lc=node<<1;

int rc=(node<<1)+1;

pushdown(node); //查詢時也要更新,以把加上的值記錄在內

int ans=0x80000000;

if(g_l<=mid)

ans=std::max(ans, query(lc, l, mid));

if(g_r>mid)

ans=std::max(ans, query(rc, mid+1, r));

return ans;

}

參考**

#include 

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using

std::cin;

using

std::cout;

using

std::endl;

const

int maxn=100005;

int tree[maxn*3];

int lazy[maxn*3];

int n,m;

void build(int node=1, int l=1, int r=n)

int mid=(l+r)/2;

build(node<<1, l, mid);

build((node<<1)+1, mid+1, r);

tree[node]=std::max(tree[node<<1], tree[(node<<1)+1]);

}void pushdown(int node)

}int g_l,g_r,g_add;

void change(int node=1, int l=1, int r=n)

int mid=(l+r)/2;

int lc=node<<1;

int rc=(node<<1)+1;

//現在要更新子結點了對吧,既然子結點的最大值還沒有加上g_add,那我們怎麼知道加了後的值是多少呢?

pushdown(node); //那就更新它,把lazy記號推下去

if(g_l<=mid)

change(lc, l, mid);

if(g_r>mid)

change(rc, mid+1, r);

tree[node]=std::max(tree[lc],tree[rc]); //記住要回來更新父結點

}//使用g_l和g_r

int query(int node=1, int l=1, int r=n)

int mid=(l+r)/2;

int lc=node<<1;

int rc=(node<<1)+1;

pushdown(node); //查詢時也要更新,以把加上的值記錄在內

int ans=0x80000000;

if(g_l<=mid)

ans=std::max(ans, query(lc, l, mid));

if(g_r>mid)

ans=std::max(ans, query(rc, mid+1, r));

return ans;

}int main()

else

if(operation==2)

}return

0;}

線段樹求區間最大值(單點修改)

問最大值 q a b 詢問 a,b 中最大值 c a b 將a點值改為b includeusing namespace std pragma warning disable 4996 define maxn 100005 define ll long long ll chushi maxn sum ...

hdu I hate it 區間最大值 線段樹

include define maxn 200001 define lson l,mid,rt 1 define rson mid 1,r,rt 1 1 int max maxn 2 int n,m int max int a,int b void pushup int rt 要理解好更新的定義,不...

數列區間最大值 線段樹

acwing 1270 time limit 2s memory limit 64mb problem description 輸入一串數字,給你 m 個詢問,每次詢問就給你兩個數字 x,y要求你說出 x 到 y 這段區間內的最大數。input output 輸出共 m 行,每行輸出乙個數。samp...