zoukankan      html  css  js  c++  java
  • 求区间最大子段和(线段树)

    填坑。。。
    线段树需要维护的是:
    左端点 x
    右端点 y (本人喜欢直接维护端点)
    [x,y]内的最大子段和 ms
    [x,y]的区间和 s
    [x,y]内的紧靠左端点的最大子段和 ls
    [x,y]内的紧靠右端点的最大子段和 rs

    困难就是,update和ask(l,r)询问[l,r]区间内的最大子段和
    那我们一步一步来

    s的维护很常规,
    ls:有两种情况:
    1.该区间内的ls是ta左儿子的ls
    2.该区间内的ls是左儿子的s+右儿子的ls
    同理,rs:有两种情况:
    1.该区间内的rs是ta右儿子的rs
    2.该区间内的rs是右儿子的s+左儿子的rs
    而ms有三种情况:
    1.该区间内的ms是左儿子的ms
    2.该区间内的ms是右儿子的ms
    3.该区间内的ms是左儿子的rs+右儿子的ls
    这里写图片描述

    void update(int bh)
    {
        tree[bh].ms=max(tree[bh<<1].ms,tree[(bh<<1)+1].ms);
        tree[bh].ms=max(tree[bh].ms,tree[bh<<1].rs+tree[(bh<<1)+1].ls);
        tree[bh].ls=max(tree[bh<<1].ls,tree[bh<<1].s+tree[(bh<<1)+1].ls);
        tree[bh].rs=max(tree[(bh<<1)+1].rs,tree[(bh<<1)+1].s+tree[bh<<1].rs);
        tree[bh].s=tree[bh<<1].s+tree[(bh<<1)+1].s;
        return;
    }

    解决一个,那询问呢~
    其实道理是一样的
    [l,r]内的区间最大子段和分以下几种情况:
    1.独立的存在于左儿子或右儿子中
    2.左儿子的rs+右儿子的ls
    然而如果[l,r]在线段树中是一个节点(我们单独维护过),那我们直接return ms 就好啦
    代码略长

    int askl(int bh,int l,int r)  //在[l,r]中查找紧靠左端的最大子段和 
    {
        if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ls;
    
        int mid=(tree[bh].x+tree[bh].y)>>1;
    
        if (r<=mid) askl(bh<<1,l,r);
        else if (l>mid) askl((bh<<1)+1,l,r);
    
        int lans=(bh<<1,l,mid);  //左儿子中紧靠左的最大子段和 
        int rans=((bh<<1)+1,mid+1,r);  //右儿子中紧靠左的最大子段和  
    
        return max(lans,rans+tree[bh<<1].s);   //rans+tree[bh<<1].s 
    }
    
    int askr(int bh,int l,int r) //在[l,r]中查找紧靠右端的最大子段和 
    {
        if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].rs;
    
        int mid=(tree[bh].x+tree[bh].y)>>1;
    
        if (r<=mid) askr(bh<<1,l,r);
        else if (l>mid) askr((bh<<1)+1,l,r);
    
        int lans=askr(bh<<1,l,mid);  //左儿子中紧靠右的最大子段和 
        int rans=askr((bh<<1)+1,mid+1,r);  //右儿子中紧靠右的最大子段和  
    
        return max(rans,lans+tree[(bh<<1)+1].s);
    }
    
    int ask(int bh,int l,int r)  //ask是专用来考虑[l,r]的最大子段和独立的存在于左子段中,
    {  //或右子段中,没有跨段的情况 
        if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ms;
    
        int mid=(tree[bh].x+tree[bh].y)>>1;  ////
    
        if (r<=mid) ask(bh<<1,l,r);
        else if (l>mid) ask((bh<<1)+1,l,r);
    
        int lans=ask(bh<<1,l,mid);
        int rans=ask((bh<<1)+1,mid+1,r);
    
        int ans=max(lans,rans);
    
        return max(ans,askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r)); 
    }  //askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r)  这是将[l,r]劈成两段,最大子段在两部分中都有一部分 
    //需要注意的是,在分成两段分别求解时,[l,r]的范围会改变 

    完整代码:

    这里写代码片
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    
    using namespace  std;
    
    const int N=100001;
    int n;
    int l,r;
    int a[N];
    struct node{
        int x,y,ls,rs,s,ms;  //ls 紧靠左边的最大子段和  rs 紧靠右边的最大子段和 
    };
    node tree[N<<2];
    
    void update(int bh)
    {
        tree[bh].ms=max(tree[bh<<1].ms,tree[(bh<<1)+1].ms);
        tree[bh].ms=max(tree[bh].ms,tree[bh<<1].rs+tree[(bh<<1)+1].ls);
        tree[bh].ls=max(tree[bh<<1].ls,tree[bh<<1].s+tree[(bh<<1)+1].ls);
        tree[bh].rs=max(tree[(bh<<1)+1].rs,tree[(bh<<1)+1].s+tree[bh<<1].rs);
        tree[bh].s=tree[bh<<1].s+tree[(bh<<1)+1].s;
        return;
    }
    
    void build(int bh,int l,int r)
    {
        tree[bh].x=l; tree[bh].y=r;
        if (l==r)
        {
            tree[bh].s=tree[bh].ms=tree[bh].ls=tree[bh].rs=a[l];
            return;
        }
        int mid=(l+r)>>1;
        build(bh<<1,l,mid);
        build((bh<<1)+1,mid+1,r);
        update(bh); 
    }
    
    void change(int bh,int mb,int z) //单点修改 
    {
        if (tree[bh].x==tree[bh].y&&tree[bh].x==mb)
        {
            tree[bh].s=tree[bh].ms=tree[bh].ls=tree[bh].rs=z;
            return;
        }
        int mid=(tree[bh].x+tree[bh].y)>>1;
        if (mb<=mid) change(bh<<1,mb,z);
        else change((bh<<1)+1,mb,z);
        update(bh);
    }
    
    int askl(int bh,int l,int r)  //在[l,r]中查找紧靠左端的最大子段和 
    {
        if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ls;
    
        int mid=(tree[bh].x+tree[bh].y)>>1;
    
        if (r<=mid) askl(bh<<1,l,r);
        else if (l>mid) askl((bh<<1)+1,l,r);
    
        int lans=(bh<<1,l,mid);  //左儿子中紧靠左的最大子段和 
        int rans=((bh<<1)+1,mid+1,r);  //右儿子中紧靠左的最大子段和  
    
        return max(lans,rans+tree[bh<<1].s);   //rans+tree[bh<<1].s 
    }
    
    int askr(int bh,int l,int r) //在[l,r]中查找紧靠右端的最大子段和 
    {
        if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].rs;
    
        int mid=(tree[bh].x+tree[bh].y)>>1;
    
        if (r<=mid) askr(bh<<1,l,r);
        else if (l>mid) askr((bh<<1)+1,l,r);
    
        int lans=askr(bh<<1,l,mid);  //左儿子中紧靠右的最大子段和 
        int rans=askr((bh<<1)+1,mid+1,r);  //右儿子中紧靠右的最大子段和  
    
        return max(rans,lans+tree[(bh<<1)+1].s);
    }
    
    int ask(int bh,int l,int r)  //ask是专用来考虑[l,r]的最大子段和独立的存在于左子段中,
    {  //或右子段中,没有跨段的情况 
        if (tree[bh].x==l&&tree[bh].y==r) return tree[bh].ms;
    
        int mid=(tree[bh].x+tree[bh].y)>>1;  ////
    
        if (r<=mid) ask(bh<<1,l,r);
        else if (l>mid) ask((bh<<1)+1,l,r);
    
        int lans=ask(bh<<1,l,mid);
        int rans=ask((bh<<1)+1,mid+1,r);
    
        int ans=max(lans,rans);
    
        return max(ans,askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r)); 
    }  //askr(bh<<1,l,mid)+askl((bh<<1)+1,mid+1,r)  这是将[l,r]劈成两段,最大子段在两部分中都有一部分 
    //需要注意的是,在分成两段分别求解时,[l,r]的范围会改变 
    
    int main()
    {
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        build(1,1,n);
        int T;
        scanf("%d",&T);
        while (T--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d",ask(1,x,y));
        }
        return 0;
    }
  • 相关阅读:
    mybatis使用*号查询数据丢失问题
    设计四个线程,其中两个线程每次对j增加1,另外两个线程对j每次减1,写出程序
    用代码实现以下程序:篮子中有10个玩具,每60秒取出3个,同时每40秒向篮子中放入1个,不断重复上述动作,当篮子中剩余玩具不足3个是,程序结束
    伽马分布的性质
    三角函数公式
    微分和积分的中值定理
    一些需要理解掌握的知识点
    一阶微分不变性
    泰勒展开和麦克劳林级数
    重要极限(1+1/n)的n次方
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673570.html
Copyright © 2011-2022 走看看