1. 程式人生 > >【演算法】線段樹

【演算法】線段樹

之前由於不會延遲標記一直沒寫這題。

那麼今天就寫一個總結來加深印象吧!

首先,延遲標記的作用就是在於當某個區間遭到改變,但又用不上(不用輸出)時。將所有先前改變都標記上去,等到要用到了,一次直接改。

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
struct segment_tree{
    int l,r;
    long long v,add;//加一個延遲標記的變數
}t[maxn*4];
long long a[4*maxn];
void build_up(int x,int l,int r){
    int mid=(l+r)/2;
    t[x].l=l;t[x].r=r;
    if (l==r){t[x].v=a[l];return ;}
       build_up(x*2,l,mid);
       build_up(x*2+1,mid+1,r);
       t[x].v=t[x*2].v+t[x*2+1].v;//標準建樹
}
void spread(int x){
    if (t[x].add){
       t[x*2].v+=t[x].add*(t[x*2].r-t[x*2].l+1);
       t[x*2+1].v+=t[x].add*(t[x*2+1].r-t[x*2+1].l+1);//更改區間值
       t[x*2].add+=t[x].add;//兩個子樹加上標記
       t[x*2+1].add+=t[x].add;
       t[x].add=0;//清空當前標記
    }
}
void change(int x,int l,int r,long long d){
     int mid=(t[x].l+t[x].r)/2;
     if (l<=t[x].l&&r>=t[x].r){//若此節點被整個更改區間覆蓋
        t[x].v+=d*(t[x].r-t[x].l+1);
        t[x].add+=d;//(接上)則可以直接更改它的值,並加上標記
        return ;
     }
     spread(x);//若沒有,則直接向下改
     if (l<=mid) change(x*2,l,r,d);
     if (r>mid) change(x*2+1,l,r,d);
     t[x].v=t[x*2].v+t[x*2+1].v;//這時再改值
}
long long ask(int x,int l,int r){
     if (l<=t[x].l&&r>=t[x].r)return t[x].v;//如果被當前區間覆蓋,就可以直接輸出值了
     spread(x);//若沒有,則向下標記
     int mid=(t[x].l+t[x].r)/2;
     long long val=0;
     if (l<=mid) val+=ask(x*2,l,r);
     if (r>mid) val+=ask(x*2+1,l,r);
     return val;
}
//void merge(int x,int y){
//     spread(t[x].l,t[x].r);
    // if (t[x].l!=t[x].r)
//}
int main(){
    int i,m,n;
    cin>>n>>m;
    for (i=1;i<=n;i++)
        cin>>a[i];
    build_up(1,1,n);
    for (i=1;i<=m;i++){
        int x,y,z;
        long long k;
        cin>>x>>y>>z;
        if (x==1){
           cin>>k;
           change(1,y,z,k);
        }
        else cout<<ask(1,y,z)<<endl;
    }
    return 0;
}