[SCOI2010] 序列操作

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

线段树用赋值和翻转懒标记维护 01 序列的计数与最长连续 1。

OJ: luogu

题目 ID: P2572

难度:提高

标签:线段树01序列懒标记区间翻转python

日期: 2026-07-16 23:59

题意

对 01 序列区间赋 0、赋 1、取反,并查询区间 1 的数量或最长连续 1。

思路

节点同时保存 1 和 0 的数量、前缀、后缀、最大连续段。翻转时交换两套统计量;赋值时直接设置对应套统计量并清除旧标记。赋值标记优先级高于翻转,向下传递时先赋值再翻转。

Python 知识

  • bytearray 保存翻转标记,assigned = -1 表示没有赋值标记。
  • array("i") 紧凑保存 0/1 的八组区间统计量。
  • 元组返回 (sum, prefix, suffix, best, length),查询部分区间可以复用同一套合并公式。
  • 对称维护 0/1 统计量,翻转操作只需交换列表中的两个值。

代码

python
import sys
from array import array


sys.setrecursionlimit(1_000_000)
input = sys.stdin.buffer.readline
n, operations = map(int, input().split())
values = list(map(int, input().split()))
ones = array("i", [0]) * (4 * n)
one_prefix = array("i", [0]) * (4 * n)
one_suffix = array("i", [0]) * (4 * n)
one_best = array("i", [0]) * (4 * n)
zeros = array("i", [0]) * (4 * n)
zero_prefix = array("i", [0]) * (4 * n)
zero_suffix = array("i", [0]) * (4 * n)
zero_best = array("i", [0]) * (4 * n)
assigned = array("b", [-1]) * (4 * n)
flipped = bytearray(4 * n)


def apply_assign(node, value, length):
    assigned[node] = value
    flipped[node] = 0
    if value:
        ones[node] = length
        one_prefix[node] = one_suffix[node] = one_best[node] = length
        zeros[node] = zero_prefix[node] = zero_suffix[node] = zero_best[node] = 0
    else:
        zeros[node] = length
        zero_prefix[node] = zero_suffix[node] = zero_best[node] = length
        ones[node] = one_prefix[node] = one_suffix[node] = one_best[node] = 0


def apply_flip(node):
    ones[node], zeros[node] = zeros[node], ones[node]
    one_prefix[node], zero_prefix[node] = zero_prefix[node], one_prefix[node]
    one_suffix[node], zero_suffix[node] = zero_suffix[node], one_suffix[node]
    one_best[node], zero_best[node] = zero_best[node], one_best[node]
    if assigned[node] != -1:
        assigned[node] ^= 1
    else:
        flipped[node] ^= 1


def pull(node, left_length, right_length):
    left, right = node * 2, node * 2 + 1
    ones[node] = ones[left] + ones[right]
    one_prefix[node] = one_prefix[left] + one_prefix[right] if one_prefix[left] == left_length else one_prefix[left]
    one_suffix[node] = one_suffix[right] + one_suffix[left] if one_suffix[right] == right_length else one_suffix[right]
    one_best[node] = max(one_best[left], one_best[right], one_suffix[left] + one_prefix[right])
    zeros[node] = zeros[left] + zeros[right]
    zero_prefix[node] = zero_prefix[left] + zero_prefix[right] if zero_prefix[left] == left_length else zero_prefix[left]
    zero_suffix[node] = zero_suffix[right] + zero_suffix[left] if zero_suffix[right] == right_length else zero_suffix[right]
    zero_best[node] = max(zero_best[left], zero_best[right], zero_suffix[left] + zero_prefix[right])


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


def push(node, left, right):
    if left == right:
        assigned[node] = -1
        flipped[node] = 0
        return
    middle = (left + right) // 2
    left_child, right_child = node * 2, node * 2 + 1
    if assigned[node] != -1:
        apply_assign(left_child, assigned[node], middle - left + 1)
        apply_assign(right_child, assigned[node], right - middle)
        assigned[node] = -1
    if flipped[node]:
        apply_flip(left_child)
        apply_flip(right_child)
        flipped[node] = 0


def update(node, left, right, query_left, query_right, kind):
    if query_left <= left and right <= query_right:
        if kind == 2:
            apply_flip(node)
        else:
            apply_assign(node, kind, right - left + 1)
        return
    push(node, left, right)
    middle = (left + right) // 2
    if query_left <= middle:
        update(node * 2, left, middle, query_left, query_right, kind)
    if middle < query_right:
        update(node * 2 + 1, middle + 1, right, query_left, query_right, kind)
    pull(node, middle - left + 1, right - middle)


def query(node, left, right, query_left, query_right):
    if query_left <= left and right <= query_right:
        return ones[node], one_prefix[node], one_suffix[node], one_best[node], right - left + 1
    push(node, left, right)
    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)
        if result is None:
            result = right_result
        else:
            left_sum, left_prefix, left_suffix, left_best, left_length = result
            right_sum, right_prefix, right_suffix, right_best, right_length = right_result
            result = (left_sum + right_sum,
                      left_prefix + right_prefix if left_prefix == left_length else left_prefix,
                      right_suffix + left_suffix if right_suffix == right_length else right_suffix,
                      max(left_best, right_best, left_suffix + right_prefix),
                      left_length + right_length)
    return result


build(1, 1, n)
answers = []
for _ in range(operations):
    kind, left, right = map(int, input().split())
    left += 1
    right += 1
    if kind < 3:
        update(1, 1, n, left, right, kind)
    else:
        result = query(1, 1, n, left, right)
        answers.append(str(result[0] if kind == 3 else result[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(log n),空间 O(n)

总结

复杂区间操作的核心是让每个懒标记都能在节点信息上“就地完成”,而不是下传到每个叶子。