小白逛公园

GitHub跳转原题关系图返回列表

线段树维护区间和、最大前缀、最大后缀和最大子段和,支持单点修改。

OJ: luogu

题目 ID: P4513

难度:普及+/提高

标签:线段树最大子段和点修改python

日期: 2026-07-16 23:59

题意

区间查询最大连续子段和,另有单点赋值操作;查询端点可能反向给出。

思路

节点保存 sum、最大前缀 prefix、最大后缀 suffix 和最大子段 best。合并左右节点时,跨中点的候选是 left.suffix + right.prefix,其余三项分别取左右最大值。查询部分区间时返回一个五元组,最后按相同公式合并。

Python 知识

  • merge 返回元组,适合把一个区间的四个统计量和长度作为整体传递。
  • array("q") 让四棵大规模 64 位统计数组保持紧凑。
  • None 表示还没有取到左子区间,避免为查询补造无效的负无穷节点。
  • max 的多个参数直接表达三种最大子段来源。

代码

python
import sys
from array import array


sys.setrecursionlimit(1_000_000)
input = sys.stdin.buffer.readline
n, operations = map(int, input().split())
values = array("q", (int(input()) for _ in range(n)))
total = array("q", [0]) * (4 * n)
prefix = array("q", [0]) * (4 * n)
suffix = array("q", [0]) * (4 * n)
best = array("q", [0]) * (4 * n)


def pull(node):
    left, right = node * 2, node * 2 + 1
    total[node] = total[left] + total[right]
    prefix[node] = max(prefix[left], total[left] + prefix[right])
    suffix[node] = max(suffix[right], total[right] + suffix[left])
    best[node] = max(best[left], best[right], suffix[left] + prefix[right])


def build(node, left, right):
    if left == right:
        total[node] = prefix[node] = suffix[node] = best[node] = values[left - 1]
        return
    middle = (left + right) // 2
    build(node * 2, left, middle)
    build(node * 2 + 1, middle + 1, right)
    pull(node)


def update(node, left, right, position, value):
    if left == right:
        total[node] = prefix[node] = suffix[node] = best[node] = value
        return
    middle = (left + right) // 2
    if position <= middle:
        update(node * 2, left, middle, position, value)
    else:
        update(node * 2 + 1, middle + 1, right, position, value)
    pull(node)


def merge(a, b):
    at, ap, ass, ab, al = a
    bt, bp, bs, bb, bl = b
    return (at + bt, max(ap, at + bp), max(bs, bt + ass),
            max(ab, bb, ass + bp), al + bl)


def query(node, left, right, query_left, query_right):
    if query_left <= left and right <= query_right:
        return total[node], prefix[node], suffix[node], best[node], right - left + 1
    middle = (left + right) // 2
    result = None
    if query_left <= middle:
        result = query(node * 2, left, middle, query_left, query_right)
    if middle < query_right:
        right_result = query(node * 2 + 1, middle + 1, right, query_left, query_right)
        result = right_result if result is None else merge(result, right_result)
    return result


build(1, 1, n)
answers = []
for _ in range(operations):
    operation, x, y = map(int, input().split())
    if operation == 1:
        if x > y:
            x, y = y, x
        answers.append(str(query(1, 1, n, x, y)[3]))
    else:
        update(1, 1, n, x, y)
print("\n".join(answers))

原有 C++ 版本仍保留:

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-16 23:46
 * update_at: 2026-07-16 23:46
 */
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    return 0;
}

复杂度

建树 O(n),每次修改或查询 O(log n),空间 O(n)

总结

最大子段和不是只存一个最大值;前缀和后缀正是跨越中点所需的边界信息。