[TJOI2009] 开关

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

用翻转懒标记维护区间开关状态和区间亮灯数量。

OJ: luogu

题目 ID: P3870

难度:普及/提高-

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

日期: 2026-07-16 23:59

题意

初始所有开关关闭,支持区间取反和查询区间内打开的开关数。

思路

节点只需保存区间内 1 的个数。翻转长度为 length 的节点时,新的数量是 length - count;两个翻转标记叠加等于没有翻转,因此懒标记用异或维护。

Python 知识

  • bytearray 适合保存只有 0/1 的懒标记。
  • tree[node] = length - tree[node] 直接完成整段取反。
  • 把所有答案放进列表,最后一次 "\\n".join 输出,减少频繁刷新。

代码

python
import sys


sys.setrecursionlimit(1_000_000)
input = sys.stdin.buffer.readline
n, operations = map(int, input().split())
tree = [0] * (4 * n)
flipped = bytearray(4 * n)


def apply(node, length):
    tree[node] = length - tree[node]
    flipped[node] ^= 1


def push(node, left, right):
    if flipped[node] and left != right:
        middle = (left + right) // 2
        apply(node * 2, middle - left + 1)
        apply(node * 2 + 1, right - middle)
        flipped[node] = 0


def update(node, left, right, query_left, query_right):
    if query_left <= left and right <= query_right:
        apply(node, 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)
    if middle < query_right:
        update(node * 2 + 1, middle + 1, right, query_left, query_right)
    tree[node] = tree[node * 2] + tree[node * 2 + 1]


def query(node, left, right, query_left, query_right):
    if query_left <= left and right <= query_right:
        return tree[node]
    push(node, left, right)
    middle = (left + right) // 2
    answer = 0
    if query_left <= middle:
        answer += query(node * 2, left, middle, query_left, query_right)
    if middle < query_right:
        answer += query(node * 2 + 1, middle + 1, right, query_left, query_right)
    return answer


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

总结

“翻转两次抵消”是布尔懒标记最典型的合并规则。