扶苏的问题

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

在线段树中同时维护区间赋值、区间加法和区间最大值。

OJ: luogu

题目 ID: P1253

难度:普及+/提高

标签:线段树懒标记区间赋值区间最大值python

日期: 2026-07-16 23:59

题意

支持把区间全部赋为 x、把区间全部加上 x,以及查询区间最大值。

思路

节点保存最大值,并维护两个标记:未下传的赋值 assigned 和追加加法 addition。赋值会覆盖旧的加法;加法若遇到已有赋值就直接改写赋值,否则累加到 addition。下传时先赋值、后加法。

Python 知识

  • bytearray 保存“是否存在赋值标记”,因为赋值本身可能是负数,不能用数值正负判断。
  • array("q") 紧凑保存最多四百万个 64 位线段树字段,控制百万规模数据的内存。
  • (set_value if operation == 1 else add_value)(...) 用函数对象选择两种更新。
  • 查询初值取很小的负数,能够正确处理全负数区间。

代码

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", map(int, input().split()))
maximum = array("q", [0]) * (4 * n)
addition = array("q", [0]) * (4 * n)
assigned = array("q", [0]) * (4 * n)
has_assignment = bytearray(4 * n)


def build(node, left, right):
    if left == right:
        maximum[node] = values[left - 1]
        return
    middle = (left + right) // 2
    build(node * 2, left, middle)
    build(node * 2 + 1, middle + 1, right)
    maximum[node] = max(maximum[node * 2], maximum[node * 2 + 1])


def set_value(node, value):
    maximum[node] = assigned[node] = value
    addition[node] = 0
    has_assignment[node] = 1


def add_value(node, value):
    maximum[node] += value
    if has_assignment[node]:
        assigned[node] += value
    else:
        addition[node] += value


def push(node, left, right):
    if left == right:
        return
    child_left, child_right = node * 2, node * 2 + 1
    middle = (left + right) // 2
    if has_assignment[node]:
        set_value(child_left, assigned[node])
        set_value(child_right, assigned[node])
        has_assignment[node] = 0
    if addition[node]:
        add_value(child_left, addition[node])
        add_value(child_right, addition[node])
        addition[node] = 0


def update(node, left, right, query_left, query_right, operation, value):
    if query_left <= left and right <= query_right:
        (set_value if operation == 1 else add_value)(node, value)
        return
    push(node, left, right)
    middle = (left + right) // 2
    if query_left <= middle:
        update(node * 2, left, middle, query_left, query_right, operation, value)
    if middle < query_right:
        update(node * 2 + 1, middle + 1, right, query_left, query_right, operation, value)
    maximum[node] = max(maximum[node * 2], maximum[node * 2 + 1])


def query(node, left, right, query_left, query_right):
    if query_left <= left and right <= query_right:
        return maximum[node]
    push(node, left, right)
    middle = (left + right) // 2
    answer = -10**30
    if query_left <= middle:
        answer = max(answer, query(node * 2, left, middle, query_left, query_right))
    if middle < query_right:
        answer = max(answer, query(node * 2 + 1, middle + 1, right, query_left, query_right))
    return answer


build(1, 1, n)
answers = []
for _ in range(operations):
    operation = list(map(int, input().split()))
    if operation[0] == 3:
        answers.append(str(query(1, 1, n, operation[1], operation[2])))
    else:
        update(1, 1, n, operation[1], operation[2], operation[0], operation[3])
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)

总结

多个懒标记共存时,先写清楚“新操作作用在旧标记之后”的复合顺序,代码就不会混乱。