1. 程式人生 > >線段樹維護區間最大連續和

線段樹維護區間最大連續和

在我的理解中,線段樹就是線段組成的樹,越靠近根部的層,線段的長度越長,一個結構體(樹的每一個點的左邊界,右邊界以及要維護的值)和三個函式(build——拆的過程),update(更新的過程,更新的時候要先二分找到最底層的區間,然後需要向上將所有的區間都更新了),query(尋找的過程)。

http://acm.uestc.edu.cn/#/problem/show/844
這個題目的意思就是給一個區間,然後求指定子區間的最大的連續和,在這個過程中還可以不斷更新資料。
這個要理清楚結構體裡要存的東西,即要維護的東西,是四個值,定義為ssum、sum_、suml,sumr。

#include<iostream>
using namespace std; int power[100005]; typedef struct Tree { int left,right; int ssum;//此區間內最大連續和 int sum_;//該節點以下的節點值得總和 int suml;//此區間得左端開始的最大連續和 int sumr;//此區間得右端開始的最大連續和 }; Tree tree[100005*4]; int num=0; void build(int root,int left,int right) { tree[root].left=left; tree[root].right=right; if
(left==right) { num=root; tree[root].ssum=power[left]; tree[root].suml=power[left]; tree[root].sum_=power[left]; tree[root].sumr=power[left]; } else { int mid=(left+right)/2; build(root*2,left,mid); build(root*2+1,mid+1,right); tree[root].sum_
=tree[root*2].sum_+tree[root*2+1].sum_; //sum_代表的是這個區間的和,那麼只要把左區間的和以及右區間的和給加起來就好 tree[root].ssum=max(max(tree[root*2].ssum,tree[root*2+1].ssum),(tree[root*2].sumr+tree[root*2+1].suml)); //這個是比較關鍵的,已知一個區間的左右區間的最大連續和怎麼合併,要麼就是左區間的最大連續和,要麼就是右區間的最大連續和,要麼就是左區間的最大右起連續和+右區間的最大左起連續和 tree[root].suml=max(tree[root*2].suml,tree[2*root].sum_+tree[2*root+1].suml); //該區間從左邊起的最大連續和,要麼就是左子區間的最大左起連續和要麼就是左子區間的和+右子區間的最大左起連續和,這也是我們維護一個區間的和的原因。 tree[root].sumr=max(tree[root*2+1].sumr,tree[2*root].sumr+tree[2*root+1].sum_); } } Tree query_sum(int root,int left,int right) { if(tree[root].left==left&&tree[root].right==right) return tree[root]; else { int mid=(tree[root].left+tree[root].right)/2; if(right<=mid) return query_sum(2*root,left,right); if(left>mid) return query_sum(2*root+1,left,right); Tree k1,k2,temp; k1=query_sum(2*root,left,mid); k2=query_sum(2*root+1,mid+1,right); temp.sum_=k1.sum_+k2.sum_; temp.ssum=max(max(k1.ssum,k2.ssum),k1.sumr+k2.suml); temp.suml=max(k1.suml,k1.sum_+k2.suml); temp.sumr=max(k2.sumr,k1.sumr+k2.sum_); return temp; } } void update(int root,int pos,int value) //更新函式就是先不斷二分找到位置,然後要像建樹一樣合併區間 { if(tree[root].left==tree[root].right) { tree[root].ssum=value; tree[root].suml=value; tree[root].sum_=value; tree[root].sumr=value; } else { int mid=(tree[root].left+tree[root].right)/2; if(pos<=mid) update(2*root,pos,value); else update(2*root+1,pos,value); tree[root].sum_=tree[root*2].sum_+tree[root*2+1].sum_; tree[root].ssum=max(max(tree[root*2].ssum,tree[root*2+1].ssum),(tree[root*2].sumr+tree[root*2+1].suml)); tree[root].suml=max(tree[root*2].suml,tree[2*root].sum_+tree[2*root+1].suml); tree[root].sumr=max(tree[root*2+1].sumr,tree[2*root].sumr+tree[2*root+1].sum_); } } int main() { int n,m; scanf("%d %d",&n,&m); int i; for(i=1;i<=n;i++) scanf("%d",&power[i]); build(1,1,n); for(i=0;i<m;i++) { int type,loperate,roperate; scanf("%d%d%d",&type,&loperate,&roperate); if(type) { update(1,loperate,roperate); } else { Tree temp=query_sum(1,loperate,roperate); printf("%d\n",temp.ssum); } } return 0; }
#include<iostream>
using namespace std;

long long power[100005];
typedef struct Tree
{
    int left,right;
    long long ssum;//此區間內最大連續和 
    long long sum_;//該節點以下的節點值得總和 
    long long suml;//此區間得左端開始的最大連續和 
    long long sumr;//此區間得右端開始的最大連續和 
};

Tree tree[100005*4]; 
int num=0;

void build(int root,int left,int right)
{
    tree[root].left=left;
    tree[root].right=right;
    if(left==right)
    {
        num=root;
        tree[root].ssum=power[left];
        tree[root].suml=power[left];
        tree[root].sum_=power[left];
        tree[root].sumr=power[left];
    }
    else
    {
        int mid=(left+right)/2;
        build(root*2,left,mid);
        build(root*2+1,mid+1,right);
        tree[root].sum_=tree[root*2].sum_+tree[root*2+1].sum_;
        tree[root].ssum=max(max(tree[root*2].ssum,tree[root*2+1].ssum),(tree[root*2].sumr+tree[root*2+1].suml));
        tree[root].suml=max(tree[root*2].suml,tree[2*root].sum_+tree[2*root+1].suml);
        tree[root].sumr=max(tree[root*2+1].sumr,tree[2*root].sumr+tree[2*root+1].sum_);
    }
}

Tree query_sum(int root,int left,int right)
{
    if(tree[root].left>=left&&tree[root].right<=right)
        return tree[root];
    else
    {
        int mid=(tree[root].left+tree[root].right)/2;
        if(right<=mid)
            return query_sum(2*root,left,right);
        if(left>mid)
            return query_sum(2*root+1,left,right);

        Tree k1,k2,temp;    
        k1=query_sum(2*root,left,mid);
        k2=query_sum(2*root+1,mid+1,right);

        temp.sum_=k1.sum_+k2.sum_;
        temp.ssum=max(max(k1.ssum,k2.ssum),k1.sumr+k2.suml);
        temp.suml=max(k1.suml,k1.sum_+k2.suml);
        temp.sumr=max(k2.sumr,k1.sumr+k2.sum_);
        return temp;

    }
}

void update(int root,int pos,int value)
{
    if(tree[root].left==tree[root].right)
    {
        tree[root].ssum=value;
        tree[root].suml=value;
        tree[root].sum_=value;
        tree[root].sumr=value;  
    }
    else
    {
        int mid=(tree[root].left+tree[root].right)/2;
        if(pos<=mid)
        update(2*root,pos,value);
        else
        update(2*root+1,pos,value);
        tree[root].sum_=tree[root*2].sum_+tree[root*2+1].sum_;
        tree[root].ssum=max(max(tree[root*2].ssum,tree[root*2+1].ssum),(tree[root*2].sumr+tree[root*2+1].suml));
        tree[root].suml=max(tree[root*2].suml,tree[2*root].sum_+tree[2*root+1].suml);
        tree[root].sumr=max(tree[root*2+1].sumr,tree[2*root].sumr+tree[2*root+1].sum_);     
    }   
} 

int main()
{
    int n;
    scanf("%d",&n);
    int i;
    for(i=1;i<=n;i++)
        scanf("%lld",&power[i]);
    build(1,1,n);
    //for(i=1;i<=num;i++)
    //  cout<<tree[i].left<<" "<<tree[i].right<<" "<<tree[i].ssum<<" "<<tree[i].sum_<<" "<<tree[i].suml<<" "<<tree[i].sumr<<endl;
    for(i=0;i<n;i++)
    {
        long long x;
        scanf("%lld",&x);
        update(1,x,-10000000000000000);
        Tree temp=query_sum(1,1,n);
        if(temp.ssum>=0)
            printf("%lld\n",temp.ssum);
        else
            printf("0\n");
    }

    return 0;
}