1. 程式人生 > >線段樹模板(單點更新,區間更新,RMQ)

線段樹模板(單點更新,區間更新,RMQ)

1.單點更新

說明

單點更新,區間求和(你問我單點求和??你就不會把區間長度設為0啊?)
• sum[]為線段樹,需要開闢四倍的元素數量的空間。
• build()為建樹操作
• update()為更新操作
• query()為查詢操作
時間複雜度:O(nlogn)

使用方法

  1. build(1, n); 建立一個葉子節點為n個的線段樹
  2. update(pos, val, 1, n); 更新樹中下標為pos的葉子節點值增加val
  3. 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)

使用方法

  1. build(1, n); 建立一個葉子節點為n個的線段樹
  2. update(l, r, val, 1, n); 更新線段樹中[l, r]區間每個值都增加val
  3. 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()為查詢操作

使用方法

  1. 根據情況修改RMQ的巨集定義
  2. build(1, n); 建立一個葉子節點為n個的線段樹
  3. update(pos, val, 1, n); 修改樹中下標為pos的葉子節點值為val
  4. 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;
}