zoukankan      html  css  js  c++  java
  • 如何分治求最大子段和

    洛谷题目链接:最大子段和

    什么是最大子段和?顾名思义,在一个序列中,找到一段,将这一段元素相加使得结果最大.

    其实可以通过递推来在O(n)的时间复杂度内求出结果,也就是判断一个数前面一个数能否对最大子段和作贡献.如果前面那个数>0,那么就将它加到这一个数里,每次正在操作的数进行取max.但是这样不能在线查询(虽然这个题也不需要在线),所以我们用线段树的方法来维护每个区间的最大子段和.

    线段树中记录几个变量:ls记录从区间左端点开始向右延伸能得到的最大子段和,rs记录从右端点开始向左延伸能得到的最大子段和,ss记录区间的最大子段和(不管是从区间中哪个位置开始),sum记录区间和.

    我们将正在合并的区间节点编号叫root,它的左端点为l,右端点为r

    那在合并ls的时候只存在这样几种情况:

    1. root左端点包含的最大子段的右端点延伸到了右儿子
    2. root左端点包含的最大子段的右端点仍然在左儿子的范围内

    合并rs也是同理.
    然后考虑如何合并ss,root的包含的最大子段的左端点叫x,右端点叫y,那么只有这样几种情况:

    1. (x = l , mid+1leq y<r)
    2. (x=l , y=r)
    3. (l<xleq mid,mid+1leq y<r)
    4. (l<xleq mid,y=r)
    5. (l<x<yleq mid)
    6. (mid+1leq x<yleq r)

    整理一下式子也就是这样:

    [ls[l,r]=max(ls[l,mid],sum[l,mid]+ls[mid+1,r]) ]

    [rs[l,r]=max(rs[mid+1,r],sum[mid+1,r]+rs[l,mid]) ]

    [ss[l,r]=max(ss[l,mid],ss[mid+1,r],ls[mid+1,r]+rs[l,mid+1]) ]

    那么我们直接对这些情况进行讨论,下面看代码注释

    #include<bits/stdc++.h>
    #define ll(x) (x<<1)
    #define rr(x) (x<<1|1)
    using namespace std;
    const int N=200000+5;
    
    int n, a[N];
    
    struct seg_tree{
    	int val, l, r, ls, rs, ss, sum;
    }t[N*4];
    
    int gi(){
    	int ans = 0 , f = 1; char i = getchar();
    	while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
    	while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
    	return ans * f;
    }
    
    void up(int x){
    	int lx = ll(x) , rx = rr(x);//宏定义
    	int m = t[lx].rs+t[rx].ls;
    	t[x].ls = max(t[lx].ls , t[lx].sum+t[rx].ls);//对区间从最左边开始维护最大连续子段
    	t[x].rs = max(t[rx].rs , t[rx].sum+t[lx].rs);
    	t[x].ss = max(m , max(t[lx].ss , t[rx].ss));//根据上面几种情况取最大值作为最大连续子段和
    	t[x].sum = t[lx].sum + t[rx].sum;//统计区间和
    }
    
    void solve(int root, int l, int r){//递归建树
    	int mid = (l+r>>1);
    	t[root].l = l , t[root].r = r;
    	if(l == r){
    		t[root].val = t[root].sum = a[l];
    		t[root].ss = t[root].ls = t[root].rs = a[l];
    		return;
    	}
    	solve(ll(root),l,mid);
    	solve(rr(root),mid+1,r);
    	up(root);//向上统计答案
    }
    
    int main(){
    	//freopen("data.in","r",stdin);
    	n = gi();
    	for(int i=1;i<=n;i++) a[i] = gi();
    	solve(1,1,n);
    	printf("%d
    ",t[1].ss);
    	return 0;
    }
    
  • 相关阅读:
    回车执行函数
    ajax短信验证码-mvc
    css3背景及字体渐变
    MVC3-表单
    Layout布局
    【leetcode】两数之和
    C语言如何开发简单的插件
    Google Supersonic列存储查询库的介绍、安装、测试
    vm网络设置
    centos升级支持到C++11, gcc4.8.2
  • 原文地址:https://www.cnblogs.com/BCOI/p/9052772.html
Copyright © 2011-2022 走看看