最大值(區間修改)
總時間限制: 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...