線段樹模板(單點更新,區間更新,RMQ)
阿新 • • 發佈:2018-12-24
1.單點更新
說明
單點更新,區間求和(你問我單點求和??你就不會把區間長度設為0啊?)
• sum[]為線段樹,需要開闢四倍的元素數量的空間。
• build()為建樹操作
• update()為更新操作
• query()為查詢操作
時間複雜度:O(nlogn)
使用方法
- build(1, n); 建立一個葉子節點為n個的線段樹
- update(pos, val, 1, n); 更新樹中下標為pos的葉子節點值增加val
- query(l, r, 1, n); 查詢[l ,r]區間值之和
###Tips
• 請注意update的目的是增減還是替換,根據情況修改update函式和pushup函式
• 建出來的樹為空樹,預設每個點值都為0,需要自行將值update上去,或者修改build中sum[rt]=0;為輸入操作scanf("%d",sum+rt);
###模版
// 有註釋版
const int maxn=2005+5;
#define lson l,m,rt<<1 //預定子左樹
#define rson m+1,r,rt<<1|1 //預定右子樹
int sum[maxn<<2];//表示節點,需要開到最大區間的四倍
void pushup(int rt){
//對於編號為rt的節點,他的左右節點分別為rt<<1和rt<<1|1
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
//造樹
void build(int l, int r,int rt=1){
//建樹操作,生成一個區間為l~r的完全二叉樹
//如果到底,則線段長度為0,表示一個點,輸入該點的值
if (l==r) {
sum[rt]=0;
return;
}
//準備子樹
int m=(l+r)>>1;
//對當前節點建立子樹
build(lson);
build(rson);
//由底向上求和
pushup(rt);
}
//更新點和包含點的枝
void update(int pos,int val,int l,int r,int rt= 1){
//pos為更新的位置 val為增加的值,正則加,負則減
//l r為區間的兩個端點值
//觸底,為一個點的時候,該節點值更新
if (l==r) {
sum[rt]+=val;
return;
}
int m = ( l + r ) >> 1;
if (pos<=m) //pos在左子樹的情況下,對左子樹進行遞迴
update(pos, val, lson);
else //pos在右子樹的情況下,對右子樹進行遞迴
update(pos, val, rson);
//更新包含該點的一系列區間的值
pushup(rt);
}
//查詢點或區間
int query(int L,int R,int l,int r,int rt=1){
// L~R為被查詢子區間 l~r為“當前”樹的全區間
if (L<=l&&r<=R) //子區間包含“當前”樹全區間
return sum[rt]; //返回該節點包含的值
int m=(l+r)>>1;
int res=0;
if (L<=m) //左端點在左子樹內
res+=query(L, R, lson);
if (R>m) //右端點在右子樹內
res+=query(L, R, rson);
return res;
}
2.區間更新
說明
區間更新,區間求和(你問我單點求和??你就不會把區間長度設為0啊?)
• sum[]為線段樹,需要開闢四倍的元素數量的空間。
• build()為建樹操作
• update()為更新操作
• query()為查詢操作
時間複雜度:O(nlogn)
使用方法
- build(1, n); 建立一個葉子節點為n個的線段樹
- update(l, r, val, 1, n); 更新線段樹中[l, r]區間每個值都增加val
- query(l, r, 1, n); 查詢[l ,r]區間值之和
###Tips
• 請注意update的目的是增減還是替換,根據情況修改update函式和pushup函式
• 建出來的樹為空樹,預設每個點值都為0,需要自行將值update上去,或者修改build中sum[rt]=0;為輸入操作scanf("%d",sum+rt);
模版
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn = 100005;
int add[maxn<<2],sum[maxn<<2];
void PushUp(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m)
{
if (add[rt])
{
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
sum[rt<<1] += add[rt] * (m - (m >> 1));
sum[rt<<1|1] += add[rt] * (m >> 1);
add[rt] = 0;
}
}
void build(int l,int r,int rt=1)
{
add[rt] = 0;
if (l == r)
{
sum[rt]=0;
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int c,int l,int r,int rt=1)
{
if (L <= l && r <= R)
{
add[rt] += c;
sum[rt] += c * (r - l + 1);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , c , lson);
if (m < R) update(L , R , c , rson);
PushUp(rt);
}
int query(int L,int R,int l,int r,int rt=1)
{
if (L <= l && r <= R)
{
return sum[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += query(L , R , lson);
if (m < R) ret += query(L , R , rson);
return ret;
}
3.RMQ
說明
RMQ:Range Minimum(Maximum) Query
• sum[]為線段樹,需要開闢四倍的元素數量的空間。
• build()為建樹操作
• update()為更新操作
• query()為查詢操作
使用方法
- 根據情況修改RMQ的巨集定義
- build(1, n); 建立一個葉子節點為n個的線段樹
- update(pos, val, 1, n); 修改樹中下標為pos的葉子節點值為val
- query(l, r, 1, n); 查詢[l ,r]區間中的RMQ
Tips
• 建出來的樹為空樹,預設每個點值都為0,需要自行將值update上去,或者修改build中sum[rt]=0;為輸入操作scanf("%d",sum+rt);
• RMQ為巨集定義,請根據情況自行修改為max或者min,對應修改query中的res為-INF或者INF
const int maxn=2005+5;
#define RMQ max
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int sum[maxn<<2]={};
void pushup(int rt){
sum[rt]=RMQ(sum[rt<<1],sum[rt<<1|1]);
}
void build(int l,int r,int rt=1){
if (l==r){
sum[rt]=0;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int pos,int val,int l,int r,int rt=1){
if (l==r) {
sum[rt]=val;
return;
}
int m=(l+r)>>1;
if (pos<=m) update(pos, val, lson);
else update(pos, val, rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt=1){
if (L<=l&&r<=R) return sum[rt];
int m=(l+r)>>1;
int res=-INF; //防負數的坑
if (L<=m) res=RMQ(res,query(L, R, lson));
if (R>m) res=RMQ(res,query(L, R, rson));
return res;
}