樹狀數組和線段樹的總結


先說樹狀數組吧  主要有lowbit,update,getsum

int lowbit(int x)
{
    return x&(-x);
}
int update(int x,int date)
{
    while(x<=y)
    {
        c[x]+=date;
        x+=lowbit(x);
    }
}
int getsum(int x)
{
    int ans=0;
    while(x>0)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}

lowbit的作用就是找到該節點的父節點或子節點   圖 (https://www.cnblogs.com/George1994/p/7710886.html)

注意了  a數組存的是原來的數  c數組的意義代表着c[i] 就是前i項的和

 

線段樹的話

差不多兩個數組搞定一個tree【n】和一個add【n】

先是建立樹

線段樹是從1這個節點開始的

void build(int l,int r,int k)//l,r為建樹的范圍 
{
    if(l==r)
    {
        cin>>tree[k];
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,k<<1);//遞歸建立數 
    build(m+1,r,k<<1|1);
    tree[k]=tree[k<<1]+tree[k<<1|1];//求和 
}

 

自我覺得這個是比較靈活的,對這個改動一下就成為不同的功能,比如最大值

void build(int l,int r,int k)
{
    if(l==r)
    {
        cin>>tree[k];
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,k<<1);
    build(m+1,r,k<<1|1);
    //tree[k]=tree[k<<1]+tree[k<<1|1];
    tree[k]=max(tree[k<<1],tree[k<<1|1]); 
}

這樣就變成了父節點 為子節點的最大值

 

然后是點更新

void update(int ee,int c,int l,int r,int k)
{
    if(l==r)
    {
        tree[k]+=c;
        //tree[k]=c;  這樣就是更改這個數,而不是加 
        return ;
    }
    int m=(l+r)>>1;
    if(ee<=m) update(ee,c,l,m,k<<1);//ee為需要更新的葉子節點,通過遞歸找 
    else update(ee,c,m+1,r,k<<1|1);
    tree[k]=tree[k<<1]+tree[k<<1|1];
}

 

然后是區間更新  ,這里需要懶標記,這個確實比較難理解

void update(int x,int y,int c,int l,int r,int k)//x,y為需要更新區間,l,r為k這一節點的管理范圍 
{
    if(x<=l&&y>=r)
    {
        tree[k]=c*(r-l+1);//這一點為c乘以這一點管理的數量
        add[k]=c;//把這一點標記一下,為懶標記
        return;//注意是結束了 
    }
    int m=(l+r)>>1;
    pushdown(k,m-l+1,r-m);//下推標記
    if(x<=m) update(x,y,c,l,m,k<<1);
    if(y>m) update(x,y,c,m+1,r,k<<1|1);//注意 這里不是else 而是都要更新並查找 
    tree[k]=tree[k<<1]+tree[k<<1|1]; 
} 
void pushdown(int k,int ln,int rn)//ln,rn為左子樹,右子樹的數字數量。 
{
    if(add[k])
    {
        add[k<<1]+=add[k];
        add[k<<1|1]+=add[k];//傳遞這個標記
        
        sum[k<<1]+=add[k]*ln;
        sum[k<<1|1]+=add[k]*rn;
        add[k]=0;
    }
} 

上面是要求和的下推標記,但如果說是這道題

POJ - 2299 

為這個區間都改變成一樣的數,而不是相加一樣的數

void pushdown(int k,int ln,int rn){
    if(add[k]){
        add[k<<1]=add[k];
        add[k<<1|1]=add[k];
sum[k
<<1]=add[k]*ln; sum[k<<1|1]=add[k]*rn; add[k]=0; } }

 

最后是區間求和

int query(int x,int y,int l,int r,int k)
{
    if(x<=l&&r<=y)
    {
        return tree[k];
    }
    int m=(l+r)>>1;
    pushdown(k,m+1-l,r-m);
    
    int ans=0;
    if(x<=m) ans+=query(x,y,l,m,k<<1);
    if(y>m) ans+=query(x,y,m+1,r,k<<1|1);
    return ans;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM