20260812 - 线段树码力大比拼 总结

前言

行呗也是成功搞上线段树了。哎不是线段树咋这么多题。?

老师老师你昨天放错题了吧,昨天所谓“马力赛”最长的代码也不过七十多行,今天随随便便就是一百多行啊,今天才是真正的马力赛啊喵……

鸭子没有马力(?,所以最后十分钟被反超了呜哇,痛失 rk1。


mmt 也说了这些了,那我今天的总结就按照这个格式来。

1. 节点存放什么内容?
2. 叶节点如何初始化?
3. 左右节点如何合并?
4. 懒标记如何作用于节点?
5. 懒标记如何合并?

没有懒标记的我会忽略后面两条。然后如果题目有特殊情况的话我会另做说明。时间复杂度懒得写了,基本都是 \(O(n \log n)\)。

以及,为了不污染大家的眼睛,今天不放代码了。)

A - Second Largest Query

不要写 A,我用半个小时的经验告诉你们,不要写 A,真的会调死你!!……

mmt 你怎么能把这题放 T1 坑人啊,这这这细节太多了哇啊。

  1. 节点存放什么内容?
    • 最大值、最大值的出现次数、次大值、次大值的出现次数。
  2. 叶节点如何初始化?
    • 将最大值设为自身,最大值出现次数为 \(1\),次大值和次大值的出现次数均设为 \(0\)。
  3. 左右节点如何合并?
    • 提取出左右儿子分别的最大值和次大值,找出其中的最大值和次大值,然后将其数量算出(如果左右儿子都含有这个数则需将数量相加),存储即可。

B - Lazy Segment Tree

  1. 节点存放什么内容?
    • \(1\) 的出现次数、\(0\) 的出现次数、该区间逆序对数量以及懒标记。
  2. 叶节点如何初始化?
    • 根据叶节点自身是 \(1\) 是 \(0\) 记录出现次数,逆序对数量和懒标记均为 \(0\)。
  3. 左右节点如何合并?
    • 出现次数直接相加,逆序对数量会多出左区间 \(1\) 数量乘上右区间 \(0\) 数量个。
  4. 懒标记如何作用于节点?
    • 交换 \(1\) 和 \(0\) 的出现次数,且逆序对数量变为该区间总有序二元组数量减去原逆序对数量减去该区间中两数相等的二元组数量,必须要注意后面加粗部分,这个大概就是我 WA 的原因了。
  5. 懒标记如何合并?
    • 每次增加懒标记时异或 \(1\),因为一个区间被翻转两次等于没翻。

C - Two Sequence Queries

  1. 节点存放什么内容?
    • 该区间的 \(A\) 值总和、\(B\) 值总和以及 \(A \times B\) 值总和。
  2. 叶节点如何初始化?
    • 分别为 \(A_i\)、\(B_i\)、\(A_i \times B_i\)。
  3. 左右节点如何合并?
    • 三个值都是直接相加就行了。
  4. 懒标记如何作用于节点?
    • 当区间同时加上 \(x\) 的 \(A\) 和 \(y\) 的 \(B\) 时,首先 \(A \times B\) 的总和会多出 \(A\) 值总和乘 \(y\) 加上 \(B\) 值总和乘 \(x\) 加上区间长度乘 \(xy\),然后 \(A\) 值总和和 \(B\) 值总和分别加上 \(x\) 和 \(y\) 乘上区间长度即可。
  5. 懒标记如何合并?
    • 累加。

D - Parenthesis Checking

基本转化:将括号序列转变为同样长度的数组,其中 ( 为 \(1\) 而 ) 为 \(-1\)。

  1. 节点存放什么内容?
    • 当前区间的总和,以及当前区间的前缀和的最小值。
  2. 叶节点如何初始化?
    • 总和和前缀最小值都是自身。
  3. 左右节点如何合并?
    • 总和直接累加即可,前缀最小值为左区间前缀最小值和左区间总和加上右区间前缀最小值取 \(\min\)。

E - Distinct Characters Queries

  1. 节点存放什么内容?
    • 当前区间内每种字母的出现次数(一个大小为 \(26\) 的桶)。
  2. 叶节点如何初始化?
    • 桶全部清零,只有当前位置的字母所对应桶为 \(1\)。
  3. 左右节点如何合并?
    • 直接累加对应值即可。

计算不同字符个数时,把出现次数的桶求出来,然后计算其中非零数个数即可。

F - Segment Tree

  1. 节点存放什么内容?
    • 当前区间的所有值的 \(\max\)。
  2. 叶节点如何初始化?
    • \(\max\) 就是叶子结点本身的值。
  3. 左右节点如何合并?
    • 对左右儿子的 \(\max\) 值取 \(\max\) 即可。

这道题中的操作 \(3\) 可以直接用二分解决,在二分的同时调用线段树求区间 \(\max\),因此时间复杂度为 \(O(n \log^2 n)\)。

G - Vacation Query

  1. 节点存放什么内容?
    • 当前区间的左右端点、当前区间内最长的连续 \(1\) 和 \(0\) 串的长度、当前区间内最长的 \(1\) 和 \(0\) 串的前缀、当前区间内最长的 \(1\) 和 \(0\) 串的后缀。
  2. 叶节点如何初始化?
    • 左右端点均为叶节点自身编号,根据叶节点自身是 \(1\) 是 \(0\) 决定串长度等信息。
  3. 左右节点如何合并?
    • 端点直接取左右节点的左右端点,连续 \(1\) 或 \(0\) 的前后缀可以直接取左儿子或右儿子的相应信息(前缀需要特殊考虑左儿子全为当前考察数值的情况,此时可以加上右儿子前缀长度,后缀同理),最长连续串长度直接对左右儿子答案、自身前后缀长度、左儿子后缀加右儿子前缀求 \(\max\) 即可。
  4. 懒标记如何作用于节点?
    • 翻转,交换所有有关 \(1\) 和 \(0\) 串的信息,\(0\) 变 \(1\),\(1\) 变 \(0\)。
  5. 懒标记如何合并?
    • 异或 \(1\),同 B 题。

H - 上帝造题的七分钟 2 / 花神游历各国

  1. 节点存放什么内容?
    • 当前区间所有值的 \(\max\)、当前区间所有值的和。
  2. 叶节点如何初始化?
    • \(\max\) 以及和均为自身。
  3. 左右节点如何合并?
    • \(\max\) 取 \(\max\),和加和。

奇怪了,题目也没问我们区间 \(\max\) 啊,维护这个干什么?而且这题是区间修改啊,我的懒标记呢?注意到这题的开平方根操作至多开 \(6\) 次就会变成 \(1\),之后再也动不了了,所以每次开平方根时可以直接深入所有没有全部为 \(1\) 的区间然后将区间内的非 \(1\) 叶节点进行修改,\(\max\) 的维护的用处也就在这里体现出来了。这样并不会导致时间复杂度爆炸,相反,其只会为我们带来 \(6\) 的常数,显然不影响啦。

I - The Child and Sequence

  1. 节点存放什么内容?
    • 当前区间所有值的 \(\max\)、当前区间所有值的和。
  2. 叶节点如何初始化?
    • \(\max\) 以及和均为自身。
  3. 左右节点如何合并?
    • \(\max\) 取 \(\max\),和加和。

你一定发现了这题上面的内容和 H 题一模一样,我不会告诉你我就是复制的,因为在这题中,有效取模一定会让数值至少缩小到原来的一半,那么至多只会进行 \(\log\) 次有效取模,(而题目又只支持单点修改),所以可以直接沿用上一题的做法,把有效取模也就是 \(\ge mod\) 的部分直接拿去暴力取模,时间复杂度多一只 \(\log\),总体是 \(O(n \log^2 n)\) 的。

总结

感觉今天的线段树主要要注意的是细节的实现,所以我们要注意在写完代码后测一些边界数据:

  1. 会让代码输出 \(0/-1/n+1\) 等边界值的数据;
  2. 如果有懒标记,一定要测区间修改后查询的数据,防止懒标记下传出错;
  3. 整个序列均为一个数等特殊情况;
  4. 对同一处位置做多次修改,看修改效果是否叠加(某些情况下修改会抵消,比如翻转区间);
  5. 修改为极大值、极小值的情况,确保对边界处理没有错误;
  6. 等等等等。

一时半会想不出来了,大概就是这些吧,通常来讲如果时间有多一定要打个暴力对拍一下,这样能够更保险。调试的时候活用“输出中间量”,但也可以先瞪眼检查个两三分钟(亲测十分有效)。然后也要注意数组大小、long long 等小数据测不到的问题哦。

原来纯手打(没有代码滥竽充数)的总结也有将近四千字啊,怪不得每天总结要写一个小时呢。

posted @ 2026-08-12 19:46  嘎嘎喵  阅读(38)  评论(1)    收藏  举报