關於線段樹的一個模板

沒錯只有一個模板

對了,聲明一下本文所用的線段樹均爲結構體式

 

1.首先這個是線段樹的簡單naive操作

他支持以下幾種操作:

1.建樹(大霧

2.單點修改

3.單點賦值

4.區間修改(加)

5.區間修改(乘)

6.單點查詢

7.區間求和

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN =100001; 
struct Segtree
{
    int l,r,mid;
    int maxn,minn;
    int w;
    int tag,tag_mul;
}seg[MAXN<<2];
int a[MAXN],ans;
void build(int l,int r,int rt)
{
    seg[rt].l = l;
    seg[rt].r = r;
    seg[rt].mid = (l+r) >> 1;
    seg[rt].tag_mul = 1;
    if(l == r)
    {
        seg[rt].w = a[seg[rt].l];
        seg[rt].maxn = seg[rt].minn = a[seg[rt].l];
        return ;
    }
    build(l,seg[rt].mid,rt<<1);
    build(seg[rt].mid+1,r,rt<<1|1);
    seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w;
    seg[rt].maxn = max(seg[rt<<1].maxn,seg[rt<<1|1].maxn);
    seg[rt].minn = min(seg[rt<<1].minn,seg[rt<<1|1].minn);
    return ;
}
void pushdown_plus(int rt)//標記下傳(加)
{
    seg[rt<<1].tag += seg[rt].tag;
    seg[rt<<1|1].tag += seg[rt].tag;
    seg[rt<<1].w += (seg[rt<<1].r - seg[rt<<1].l + 1) * seg[rt].tag;
    seg[rt<<1|1].w += (seg[rt<<1|1].r - seg[rt<<1|1].l + 1) * seg[rt].tag;
    seg[rt].tag = 0;
    return ;
}
void pushdown_mul(int rt)//標記下傳(乘)
{
    seg[rt<<1].tag += seg[rt].tag;
    seg[rt<<1|1].tag += seg[rt].tag;
    seg[rt<<1].tag_mul = seg[rt<<1].tag_mul * seg[rt].tag_mul;
    seg[rt<<1|1].tag_mul = seg[rt<<1].tag_mul * seg[rt].tag_mul;
    
    seg[rt<<1].w = (seg[rt<<1].w * seg[rt].tag_mul + (seg[rt<<1].r - seg[rt<<1].r + 1) * seg[rt].tag);
    seg[rt<<1|1].w = (seg[rt<<1|1].w * seg[rt].tag_mul + (seg[rt<<1|1].r - seg[rt<<1|1].l + 1) * seg[rt].tag);
    
    seg[rt].tag = 0;
    seg[rt].tag_mul = 1;
    return ;
}
void update_plus(int l,int r,int val,int rt)//區間修改(加)
{
    if(seg[rt].l >= l && seg[rt].r <= r)
    {
        seg[rt].tag += val;
        seg[rt].w += (seg[rt].r - seg[rt].l + 1) * val;
        return ; 
    }
    if(l <= seg[rt].mid)
    update_plus(l,seg[rt].mid,val,(rt<<1));
    if(r > seg[rt].mid)
    update_plus(seg[rt].mid+1,r,val,(rt<<1|1));
    seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w;
    return ;
}
void update_mul(int l,int r,int val,int rt)//區間修改(乘)
{
    if(seg[rt].r < l || seg[rt].l > r)
    return ;
    if(seg[rt].l <= l && seg[rt].r >= r)
    {
        seg[rt].w = seg[rt].w * val;
        seg[rt].tag_mul = seg[rt].tag_mul * val;
        seg[rt].tag = seg[rt].tag * val;
        return ;
    }
    pushdown_mul(rt);
    update_mul(l,seg[rt].mid,val,rt<<1);
    update_mul(seg[rt].mid+1,r,val,rt<<1|1);
    seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w;
    return ;
}
void query(int l,int r,int rt)//區間求和 
{
    if(seg[rt].l >= l && seg[rt].r <= r)
    {
        ans += seg[rt].w;  //注意每一次在main內詢問完畢之後要將ans重置 
        return ;
    }
    if(seg[rt].tag)
    pushdown_plus(rt);
    if(seg[rt].tag_mul)
    pushdown_mul(rt);
    if(l <= seg[rt].mid)
    query(l,r,(rt<<1));
    if(r > seg[rt].mid)
    query(l,r,(rt<<1|1));
    return ;
}
void modify(int x,int val,int rt)//單點修改x位置爲val
{
    if(seg[rt].l == seg[rt].r == x)
    {
        seg[rt].maxn = seg[rt].w = val;
        return ;
    }
    if(x <= seg[rt].mid)
    modify(x,val,rt<<1);
    else
    modify(x,val,rt<<1|1);
    seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w;
    seg[rt].maxn = max(seg[rt<<1].maxn,seg[rt<<1|1].maxn);
    return ;
}
int main()
{
    cout << "這是線段樹的naive操作";
    return 0;
}

 

 

2.區間求最大子段和

這個其實也只是線段樹功能的一部分rwr

考慮每一個區間的最大子段和一共有三種存在方式:全部被包含在左側,全部被包含在右側,被左側和右側各包含一部分

對於前兩種情況就是向左邊和右邊的子區間遞歸就行了,

對於第三種情況:我們對每一個線段樹的"區間點"維護一個最大前綴和和最大後綴和,(在這裏最大前綴和就是包含最左邊的點的最大子段和,最大後綴和就是包含最右邊的點的最大子段和)

那麼第三種情況就是該區間的左側區間最大後綴和加上右側區間的最大前綴和

以該圖爲例,則第一、二、三種情況分別對應了紅色、黃色、綠色的區間

最大前綴和就是藍色的區間、最大後綴和就是橙色的區間

 

 會轉移了就好寫了把,貼代碼了:

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 50001;
struct Segtree
{
    int l;
    int r;
    int mid;
    int w;
    int prel;
    int prer;
    int res;
}seg[MAXN<<2];
int a[MAXN],n,m;
void pushup(int rt)
{
    seg[rt].w = seg[rt<<1].w + seg[rt<<1|1].w;
    seg[rt].prel = max(seg[rt<<1].prel,seg[rt].w + seg[rt<<1|1].prel);
    seg[rt].prer = max(seg[rt<<1|1].prer,seg[rt<<1|1].w + seg[rt<<1].prer);
    seg[rt].res = max(seg[rt<<1].prer + seg[rt<<1|1].prel,max(seg[rt<<1].res,seg[rt<<1|1].res));
}
void build(int l,int r,int rt)
{
    seg[rt].l = l;
    seg[rt].r = r;
    if(l == r)
    {
        seg[rt].res = seg[rt].prel = seg[rt].prer = seg[rt].w = a[seg[rt].l];
        return ;
    }
    seg[rt].mid = (seg[rt].l + seg[rt].r) >> 1;
    build(seg[rt].l,seg[rt].mid,rt<<1);
    build(seg[rt].mid + 1,seg[rt].r,rt<<1|1);
    pushup(rt);
    return ;
}
void modify(int x,int rt,int val)
{
    if(seg[rt].l == seg[rt].r)
    {
        seg[rt].prel = seg[rt].prer = seg[rt].res = seg[rt].w = val;
        return ;
    }
    int mid = (seg[rt].l + seg[rt].r) >> 1;
    if(x <= mid)
    modify(x,rt<<1,val);
    else
    modify(x,rt<<1|1,val);
    pushup(rt);
}
/*Segtree query(int l,int r,int rt)//這個是窩寫炸了的東西 
{
    if(l <= seg[rt].l && seg[rt].r <= r)
    return seg[rt];
    int mid = (l + r) >> 1;
    if(r <= mid)
    return query(l,r,rt<<1);
    if(mid < l)
    return query(l,r,rt<<1|1);
    Segtree L = query(l,seg[rt].mid,rt<<1);
    Segtree R = query(seg[rt].mid + 1,r,rt<<1|1);
    Segtree res;
    res.w = L.w + R.w;
    res.prel = max(L.prel,L.w + R.prel);
    res.prer = max(R.prer,R.w + L.prer);
    res.res = max(L.prer + R.prel,max(L.res,R.res));
    return res;
}*/
Segtree query(int x,int y,int rt,int l,int r)
{
    if(x <= l && r <= y)
    return seg[rt];
    int mid = (l + r) >> 1;
    if(y <= mid)
    return query(x,y,rt<<1,l,mid);
    if(mid<x)
    return query(x,y,rt<<1|1,mid+1,r);
    Segtree L = query(x,mid,rt<<1,l,mid);
    Segtree R = query(mid+1,y,rt<<1|1,mid+1,r);
    Segtree res;
    res.w = L.w + R.w;
    res.prel = max(L.prel,L.w + R.prel);
    res.prer = max(R.prer,R.w + L.prer);
    res.res = max(L.prer + R.prel,max(L.res,R.res));
    return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
    build(1,n,1);
    scanf("%d",&m);
    while(m--)
    {
        int opt,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        if(opt == 0)
        {
            modify(x,1,y);
        }
        else
        {
            int a6954717 = query(x,y,1,1,n).res;
            printf("%d\n",a6954717);
        }
    }
    return 0;
}

 

 

 3.這個東西我不太會,瞎胡了

題目要求是詢問區間和,同時在修改的時候是修改等差數列的

比如說修改[1,4] 4 2 ,則其實際是將a[1]+=4,a[2]+=6,a[3]+=8,a[4]+=16;

這裏我們討論對着一個題的線段樹解法:

普通的線段樹是將其設置爲lazy_tag的下傳

所以我們仍然考慮如何變形lazy_tag從而使得其仍滿足

答案是顯然的,我們可以保存該等差數列的a1和d從而使得其可以通過等差數列求和公式而得出結論

a1可以直接下傳,d=(seg[rt].r-seg[rt].l+1),這樣就行了

懶得寫代碼了rwr

 

4.這個和上一個差不多

題目要求是詢問區間和,同時在修改的時候是修改斐波那契數列的

這個題是以長者的p138 T4 爲原型的

 

 

 

 

 同上一個題,可以發現

a  (1,0)  (1,0)

b  (0,1)  (1,1)

a+b  (1,1)  (2,2)

a+2b  (1,2)  (3,4)

2a+3b  (2,3)  (5,7)

3a+5b  (3,5)  (8,12)

不管是什麼數列,要求滿足一個條件就可以用線段樹了:兩個序列的和仍然爲同一性質的序列

以上

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章