关于动态最大子段和--线段树查询

question:

有n个数,a[1]到a[n]。

接下来q次查询,每次动态指定两个数l,r,求a[l]到a[r]的最大子段和。

子段的意思是连续非空区间。

输入描述  Input Description

第一行一个数n。

第二行n个数a[1]~a[n]。

第三行一个数q。

以下q行每行两个数l和r。

输出描述  Output Description

q行,每行一个数,表示a[l]到a[r]的最大子段和。

样例输入  Sample Input

7
2 3 -233 233 -23 -2 233
4
1 7
5 6
2 5
2 3

样例输出  Sample Output

441
-2
233
3

 

高效完成这个问题,可以用线段树进行区间动态查询最大字段和

 

最大字段和

线段树实现

线段树维护:

 1. 区间最大字段和

 2. 从该区间的左端点开始的最大字段和(一定包括该区间左端点)

 3.   从该区间的右端点开始的最大字段和(同理一定包括该区间右端点)


最大字段和分布

 1. 完全的包含于做区间

 2. 完全的包含于右区间

 3. 包含于左右区间

维护的2,3主要是用于处理分布为3的情况

合并:很好理解

查询:

当查询区间完全处于当前区间的左侧

  查询当前区间的左儿子

当查询区间完全处于当前区间的右侧

  查询当前区间的由儿子

否则说明查询区间包含当前区间的mid

  将ans等置为0,从当前区间的左右儿子开始查询

关于动态最大子段和--线段树查询_第1张图片

 会在3个黑圈处执行区间完全包含

从左到右依次编号1,2,3

会在1,2号return后进行一次ans, l_ans, r_ans 的赋max值

会在3好return后进行行一次ans, l_ans, r_ans 的赋max值

当递归到2,3节点时(注意这里是节点),进行一次

ans = max(max(L_ans, R_ans), L_rans + R_lans);
l_ans = max(l_ans, T[jd << 1].w + R_lans); 
r_ans = max(r_ans, T[jd << 1 | 1].w + L_rans);

结束后ans即为answer

#include 
#include 
#include 
#include 
#include 

using namespace std;
const int N = 4e6 + 1;

#define LL long long
#define zero 0

struct Node{
    int l, r, w, max, l_max, r_max;
};
Node T[N];
int n, m;

struct R{
    inline int reint(){
        int x = 0, f = 1; char c = getchar();
        while(c < '0' || c > '9'){
            if(c == '-') f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    inline LL rell(){
        LL x = 0, f = 1; char c = getchar();
        while(c < '0' || c > '9'){
            if(c == '-') f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
}re;

void un(int jd){
    T[jd].max = max(max(T[jd << 1].max, T[jd << 1 | 1].max), T[jd << 1].r_max + T[jd << 1 | 1].l_max);
    T[jd].l_max = max(T[jd << 1].l_max, T[jd << 1].w + T[jd << 1 | 1].l_max);
    T[jd].r_max = max(T[jd << 1 | 1].r_max, T[jd << 1 | 1].w + T[jd << 1].r_max);
    T[jd].w = T[jd << 1].w + T[jd << 1 | 1].w;
    return ;
}

void build_tree(int l, int r, int jd){
    T[jd].l = l;
    T[jd].r = r;
    if(l == r){
        T[jd].w = re.reint();
        T[jd].l_max = T[jd].r_max = T[jd].max = T[jd].w;
        return ;
    }
    int mid = (l + r) >> 1;
    build_tree(l, mid, jd << 1);
    build_tree(mid + 1, r, jd << 1 | 1);
    un(jd);
}

void Q_ask(int l, int r, int jd, int x, int y, LL & ans, LL & l_ans, LL & r_ans){
    if(x <= l && r <= y){
        ans = T[jd].max;
        l_ans = T[jd].l_max;
        r_ans = T[jd].r_max;
        return ;
    }
    int mid = (l + r) >> 1;
    if(y <= mid) Q_ask(l, mid, jd << 1, x, y, ans, l_ans, r_ans);
    else if(x > mid) Q_ask(mid + 1, r, jd << 1 | 1, x, y, ans, l_ans, r_ans);
    else{
        LL L_ans,L_lans, L_rans, R_ans, R_lans, R_rans;
        L_ans = L_lans = L_rans = R_ans = R_lans = R_rans = zero;
        Q_ask(l, mid, jd << 1, x, y, L_ans, L_lans, L_rans);
        Q_ask(mid + 1, r, jd << 1 | 1, x, y, R_ans, R_lans, R_rans);
        ans = max(max(L_ans, R_ans), L_rans + R_lans);
        l_ans = max(l_ans, T[jd << 1].w + R_lans); 
        r_ans = max(r_ans, T[jd << 1 | 1].w + L_rans); 
    }
}

int main(){
    n = re.reint();
    build_tree(1, n, 1);
    m = re.reint();
    for(int i = 1; i <= m; i ++)
    {
        int x = re.reint();
        int y = re.reint();
        LL ans, l_ans, r_ans;
        ans = l_ans = r_ans = zero;
        Q_ask(1, n, 1, x, y, ans, l_ans, r_ans);
        printf("%lld\n", ans);
    }
    
    return 0;
}

 

转载于:https://www.cnblogs.com/shandongs1/p/7757878.html

你可能感兴趣的:(关于动态最大子段和--线段树查询)