UOJ Logo liu_cheng_ao的博客

博客

【欢迎投稿】「思考题研讨」这一训练形式的介绍以及例题

2025-11-07 22:05:37 By liu_cheng_ao

个人著作权声明:严禁任何未经本人(刘承奥,常用笔名/网名:蔡德仁 CommonAnts LCA liu_cheng_ao)书面授权者在梦熊联盟,或者任何虚假宣传或不实营销炒作或不正当竞争行为严重的 OI 机构的课程内或交流平台(包括但不限于品牌集训线下讨论,交流群,OJ,公众号,视频号等)上引用、传播、讨论此内容,以及本人于2024年5月及之后发布的所有内容,包括声明为公开的内容在内。

本文在 LibreOJ 最快更新!

所谓 「思考题研讨」,是一种在大学中亦有记载的算法学习方式。每个思考题是一系列相关的具体算法数学问题,围绕一个技巧/数学概念/动机等小套路展开,用于补足编程题不能表达的内容。毕竟编程题只能表达编程内容。每个思考题应当包括至少一道 OI 题或一个经典数学概念。

思考题的通常用法是几个同学小组讨论做法,之后再讲解或者看题解。当然,如果没有同学的话单人学习也是可以的。

同学们偶尔想到的那些不太实用/偏/怪/卡常/重复造轮子的灵感非常适合造思考题研讨

笔者这一年来(截至 2025 年 11 月)协同助教和网友已经造了思考题 80 余道。以下展示一些笔者认为较为不错的思考题作为例子,欢迎大家讨论!

笔者造的和征集的思考题中优质的都会在之后的教材编写、公开资源推荐等中陆续公开。也欢迎各位投稿的同学自行公开传播自己造的思考题。

目前只造了 NOIP+ 和 NOI 难度的思考题,不过该形式在入门和普及学习简单编程基础和数学知识也是有用的,欢迎各位集思广益。

有奖征集思考题:【欢迎投稿】有奖征集 OI 小知识点,技巧和思考题,包括“广为人知”但大纲未收录的内容!

思考题题例(题解之后公布)

  • 以下例题中题号 AP 是面向 NOIP+ 难度;题号 A 是面向 NOI 难度。注意因为思考题默认是要多人研讨的,会比同阶段 OJ 题更难。
  • [bonus] 是开放性或者困难的问题,给水平特别高的同学进阶讨论。

A101 大鱼吃小鱼

有一排 $n$ 条鱼,每条鱼有一个体型值(正整数)$a_i$,一条鱼可以吃掉相邻的鱼,如果被吃的鱼体型小于等于自己。吃完后自己的体型会加上被吃的鱼的体型。鱼之间位置的先后顺序不变。

问题1. [ARC189D]每条鱼最多可以吃到多大的体型?

问题2. [P9530]单点修改 $a_i$,区间查询 $[l,r]$ 内有多少条鱼可以吃完区间内其它所有鱼。(只有这个问题有修改和查询)

问题3. 如果我们有一种道具,鱼每用一次道具可以吃掉任意一条相邻的鱼,无视体型,请你对于每条鱼求出,它吃完所有其它鱼最少需要多少个道具。

问题4. 如果我们把吃鱼的规则改为只能吃右边(编号更大)和自己相邻的鱼(道具不受影响),问题2和3的结果会有什么变化?(你不能先操控别的鱼吃鱼,再用道具吃它)

问题5. 如果我们允许小鱼吃大鱼,也就是定义常数 $k(k\ge 0)$,将题目条件改为“一条鱼可以吃掉相邻的鱼,如果被吃的鱼体型小于等于自己的体型加 $k$”,问题3的结果会有什么变化?

问题6 [JS2024/U477940]:如果我们去掉问题5中 $(k\ge 0)$ 的限制,问题5的结果会有什么变化?

问题7. 如果我们把问题5和6中的条件从差值改成比例,也就是定义常数 $c(c\gt 0)$,将题目条件改为“一条鱼可以吃掉相邻的鱼,如果被吃的鱼体型小于等于自己的体型乘 $c$”,问题5和6的结果会有什么变化?

问题8.[bonus] 尝试找到一个有意思的问题并改编一个 OI 题目。例如:如果我们把鱼放到树上,定义每条鱼占据一个树上连通块,两条鱼相邻当且仅当占据的连通块相邻,初始每条鱼占据一个点,被吃的鱼的连通块会被吃它的鱼占据,会发生什么?

A102 循环博弈

有 $n$ 个人在玩游戏,有一堆初始总数为 $m$ 的石子,从第 $1$ 个人开始轮流取(第 $n$ 个人后再次轮到第 $1$ 个),每次每个人需要拿走至少 $1$ 个至多 $k$ 个石子。如果轮到一个人时石子没有了,游戏结束。定义游戏的结果为游戏结束时轮到的人的编号。

现在有一个 $n$ 行 $n$ 列的矩阵 $A$,其中 $A_{i,j}$ 表示如果游戏结果为 $i$,第 $j$ 个人会获得 $A_{i,j}$ 的收益。保证 $A$ 每一列的元素互不相等,不妨设 $A$ 的每一列都是 $1$ 到 $n$ 的排列。

每个人都想最大化自己在游戏结束后的收益,他们也知道所有人都这么想。

问题1. 讨论里所有人都最大化自己的收益在实际游戏中会怎么决策,说明每个人有确定的决策。

(并不是所有博弈都有符合直觉的确定的最优策略。例如,在一些多人游戏中经常发生某个人已经必败了但他可以选择其它人谁赢。这时你不能仅凭每个人都最大化自己收益来确定他的选择。想想这个题为什么没有类似情况。)

问题2. 给出一个关于 $n,m,k$ 的多项式时间的算法求出最优策略。

定义 $f(x,y)$ 表示轮到第 $x$ 个人且石子还剩 $y$ 个时,所有人都采取最优策略的情况下游戏的结果。

问题3. 证明 $f$ 关于石子个数存在周期。换言之,存在正整数 $T$ 使得不满足 $f(x,y)=f(x,y+T)$ 的正整数 $(x,y)$ 只有有限多对。

问题4. 考察 $n\le 3$ 时 $f$ 的情况。例如考虑:不满足周期的最大数对的上界是多少?最小正周期的上界是多少?如何构造?$f(1,\cdot)$ 和 $f(2,\cdot)$ 两列值有什么关系?(将 $k$ 视为参数)

在此基础上提出一个尽可能高效的算法计算 $n\le 3$ 时的这个问题。

问题5. 现在取消对 $n$ 的限制,考察 $f$ 的最小正周期和非周期部分的上界,并给出使之渐进尽量大的构造。(将 $n,k$ 视为参数)

这个问题比较困难,所以我们给出一些提示。另外,不要忘记打表观察也是一种方法。

混循环数列不容易处理,我们考虑纯循环,也就是说,令 $g(x,y)$ 是一个函数对任意整数 $y$ 和 $1\le x\le n$ 都满足 $g(x,y)=g(x,y+T)$ 且存在 $M$ 使得 $\forall y \gt M, g(x,y)=f(x,y)$。研究 $g$ 也可以得到循环节的性质。

例如:记 $g_x$ 表示数列,$g_x[y]=g(x,y)$(下标 $y$ 是包括负数在内的全体整数)。考虑:

问题5.1. $g_x$ 和 $g_{(x \bmod n)+1}$ 两个数列之间有什么递推关系?

问题5.2. 根据 5.1 的递推关系,如何直观刻画 $g_x$ 更方便?

问题5.3. 我们不妨设 5.1 的递推关系为 $g_x=P(g_{(x \bmod n)+1})$,那么我们知道 $P^n(g_x)=g_x$,也就是 $P^n=I$ 作用是保持不变,尝试通过 $P^n$ 的这一性质来研究周期的性质。

问题5.4. 我们不妨设原本的序列也有规则相同(但定义域不同)的递推 $f_x=P(f_{(x \bmod n)+1})$,尝试通过 $P$ 反复迭代过程中 $f$ 的变化过程研究非周期部分的上界。

问题6. 找到原问题的一个真正的多项式时间算法,也就是关于输入长度的多项式,即使 $m,k$ 是高精度的。给出你算法的时间复杂度上界。

问题7.[bonus] 研究这个问题的一些特殊情况或拓展情况,并尝试出题。例如:如果我们规定矩阵 $A$ 的值是 $A_{i,j}=(i+j-2)\bmod n+1$ 会发生什么?如果 $k=\infty$ 会发生什么?如果 $A$ 的一列内有多个相同数字,会对最优策略的概念产生什么影响?

A103 全局平衡

你有 $n$ 个数 $a_1,a_2,\dots ,a_n$。现在你要构造出一棵 $n$ 个叶子的有根二叉树,把这些数分别填入每个叶子。定义一个叶子走到根的代价等于它填的数加上到根距离。(树没有边权)

你希望让所有叶子走到根的最大代价尽可能小,并求出这个最小值。

问题1. 假如这些数是 $n/x$ 个 $x$ 和 $n-n/x$ 个 $1$($x$ 是一个参数),你能求出问题答案大致的大小吗?

问题2. 输入这 $n$ 个数,请你设计算法并写一个程序求解这个问题。

问题3.

我们知道在二叉搜索树(有根有序二叉树)上,每个子树对应中序遍历的一个区间。

现在,你有 $n$ 个点和 $m$ 个区间约束 $[l_i,r_i],1\le l_i\le r_i\le n$。你要构造一个二叉搜索树,使得在树的中序遍历上,每个约束区间 $[l_i,r_i]$ 都恰好对应某一棵子树里的所有点。

问题3.1:输入 $m$ 个区间,输出是否存在这样的二叉搜索树。

问题3.2:输入 $m$ 个区间,构造一棵深度尽量小的满足条件的二叉搜索树。

问题4.

我们知道在一些数据结构问题中,经常出现这么一种“树套树”的情况:我们有很多个二叉搜索树,当我们查找到上层的二叉搜索树的叶子时,我们需要转到这个叶子关联的下层二叉搜索树,继续递归查找。或者反过来,从一个叶子不断向上跳,当跳到根时,根可能关联了一个上层二叉搜索树的叶子,我们需要转到那个叶子继续向上跳。

例如,在树链剖分中,如果我们给每个链开一个二叉查找树维护链上的所有点,查询一个点在原树上到根路径的过程就可以描述为不断在当前链对应的二叉搜索树上向上跳,到搜索树根之后再跳到当前链的链顶在原树上的父节点。这个点属于另一条链,然后我们继续在它的链搜索树上往上跳。循环往复直到跳到原树根节点所在的链的搜索树根。

现在给定一个有根树的树链剖分方式,所有 $n$ 个树上的点被分成若干个祖先到子孙方向的链。请你找出一种二叉查找树形状的设置方法,使得在最坏情况下查询一个点在原树上到根路径的过程向上跳的步数尽可能少。你可以以此分析一下,假如树链剖分方式分别采用重链剖分和长链剖分,跳的步数复杂度是多少?

(在重链剖分时,这个算法在OI中被称为“全局平衡二叉树”)

问题5.

现在我们看这样一个问题:

你和小明玩猜数游戏。有一张纸上面写了一些实数(设为数集 $S$)。一开始,小明选一个在集合 $S$ 里的数记在心里。然后每次你可以做两种询问: - 询问一个整数 $x$,小明回答他的数和 $x$ 的大小关系。 - 询问一个实数 $x$,小明回答他的数是否等于 $x$。

请你设计一个足够优的策略使得最坏情况下询问次数尽可能少(渐进足够少即可),找出你表示这个询问次数需要从 $S$ 中提取的关键信息/参数。并且给出最坏情况下询问次数的估计。

问题6.

在问题5的基础上,你和小明从猜数升级到了猜点。

有一个平面上的点集 $S$(不妨设为用两维实数坐标表示)。

一开始,小明选一个在集合 $S$ 里的点记在心里。然后每次你可以做两种询问: - 询问一个网格直线 $x$,小明回答他的数在直线 $x$ 的哪一侧,或者恰好在直线上。(指水平或垂直且坐标为整数的直线) - 询问一个任意直线 $x$,小明回答他的点是否在这条直线上。

请你设计一个足够优的策略使得最坏情况下询问次数尽可能少(渐进足够少即可),找出你表示这个询问次数需要从 $S$ 中提取的关键信息/参数。并且给出最坏情况下询问次数的估计。当然,这个问题可以对两维分别做一次问题5,你能做到更优吗?

A032 中位数和第 k 小

(本问题中的中位数,在数字个数为偶数 $n$ 时指从小到大排名第 $\lceil n/2\rceil$ 的数,类似概念均如此取整,以保证中位数一定在输入之中。否则对于字符串之类可以排序但不能平均的对象无法定义中位数。)

问题1. 小明得到了一个程序,它可以输入一个长为 $n$ 的数组,然后在 $O(n)$ 时间内计算出这个数组内中位数的下标并返回。小明可以任意次调用这个程序,现在他想设计一个算法,输入 $n$ 个数和整数 $k$,在 $O(n)$ 时间内输出其中从小到大排第 $k$ 小的数,能做到吗?

如果把计算中位数改成随机取一个数,会对你的程序效率产生什么影响?是否存在一个特定输入总能“卡掉”该算法?

问题2. 小明构思了两个程序,程序1输入一个长为 $n$ 的数组,然后在 $O(n)$ 时间内计算出这个数组内中位数的下标。程序2输入一个长为 $n$ 的数组,在 $O(n)$ 时间内输出其中从小到大排第 $\lceil rn\rceil$ 小的数的下标,其中常数 $r$ 是一个区间 $(0,1)$ 内的固定的比例参数。

如果小明已经有了程序1,如何利用程序1设计出程序2?

如果小明已经有了程序2,如何利用程序2设计出程序1?

你的办法的时间的常数系数和 $r$ 有什么关系?

问题3. 小明突发奇想,他把 $n$($n$ 很大)个数分成了 $n/999$ 组,每组 $999$ 个数。然后他计算出了每组内部的中位数(这可以在 $O(n)$ 时间内暴力完成,毕竟 $999$ 只是个常数),又计算出了这 $n/999$ 个中位数 $m_1,m_2,\dots ,m_{n/999}$ 的中位数 $M$。请问这个“中位数的中位数”$M$ 在最开始的数组中最大和最小的排名范围大致是多少?(不必考虑多个数相等的情况,可以认为是取了更优的一个)

如果“计算 $n/999$ 个数的中位数”这一步递归计算,时间复杂度是多少?(提示:为了可以递归,不妨把整个问题理解为求第 $k$ 小,而不只是中位数)

问题4. [median of medians] 根据问题3设计一个在最坏情况下渐进复杂度低的中位数算法,并思考有多个输入数相等时的运行情况。当然,这可以用于问题1和问题2。

问题5. 现在你有 $n$ 个,每个长为 $m$ 的递增序列,你想要选出所有序列合在一起的第 $m$ 小元素。请你给出一个时间复杂度尽可能小的做法。(不考虑读入时间。)

问题6. 请你解决 https://uoj.ac/contest/91/problem/891

问题7.[bonus] 你有一个 $n\times n$ 的递增矩阵($a[i][j]\lt a[i+1][j]$ 且 $a[i][j]\lt a[i][j+1]$),你想要选出矩阵的第 $n$ 小元素。请你给出一个时间复杂度尽可能小的做法。(不考虑读入时间。)

拓展阅读.[bonus] https://www.cnblogs.com/zkyJuruo/p/18428124

A029 无删除双指针

你在一个 $n\times n$ 的网格上,初始在左上角 $(1,1)$,设 $(x,y)$ 表示从上往下第 $x$ 行从左往右第 $y$ 列的格子。这个网格中每个格子都是黑白两种颜色之一,并且白格的左、上方都是白格,黑格的右、下方都是黑格。你可以且只能看到你当前所在的格子的颜色(当然你可以思考和记录之前看到的东西)。你想搞清楚整个网格每个格子的颜色。

问题1.1. 如果你可以向四个方向的相邻格走,请你给出一个步数上界尽可能少的算法。

问题1.2. 如果你只能向左右下三个方向的相邻格走,请你给出一个步数上界尽可能少的算法。

问题1.3. 现在网格的地面倾斜了,左上方高右下方低。因此,你只能向右或者向下的相邻格走。当然只有这样是不可能探索整个网格的。所以你拿来了一条绳子,它被固定在你经过的每个格子里,这样,你就可以顺着绳子攀爬,收回最后一格绳子同时返回绳子的上一格。(也就是说任何时候绳子都是一条从 $(1,1)$ 沿着你之前走的路径向右向下到达你当前位置的折线)

问:如果你想要到达对角线($x+y=n$)上的每个格子 $(x,y)$ 一次,总共至少要走多少步?

问题1.4. 在上一问的基础上,现在保证网格只有对角线附近未知,$(x,y)$ 所有满足 $x+y\le n-10$ 的都是白格,所有满足 $x+y\ge n+10$ 的都是黑格。请你给出一个步数上界尽可能少的算法。把 $10$ 改为 $k$(仍然保证 $k$ 远小于 $n$),估计步数。

问题1.5. 在上一问的基础上,取消对角线的保证,请你给出一个步数上界尽可能少的算法。

问题2. [baka's trick]你有一排 $n$ 个物品和常数 $W,V$,第 $i$ 个物品的重量为 $w_i$,权值为 $v_i$。称一个下标区间是合法的当且仅当区间内可以选出一个总重量 $\le W$ 且总权值 $\ge V$ 的子集。容易证明如果一个区间合法则包含它的区间都合法,请你求所有极小的合法区间。$1\le n\le 10^4,1\le w_i,W\le 10^4,1\le v_i,V\le 10^9$

问题3. 有 $n$ 个点 $m$ 条边的无向图,点编号 $1\dots n$,边编号 $1\dots m$。请你求出所有极大无环区间,无环区间的定义是只考虑编号在该区间内的边不会形成环。(不要使用 LCT 乃至动态图连通性等复杂数据结构直接维护。)

问题4. 请你解决 https://uoj.ac/problem/693

A027 数对/图的zig-zag构造

问题1. 构造一个 $n$ 阶排列,使得每一对相邻数的差恰好取遍 $1,2,\dots ,n-1$。

有一个 $n$ 个点的有向完全图 $G$,它的边有 $n(n-1)$ 条。在很多构造问题中,我们需要做这样一件事:把这 $n(n-1)$ 条边分成 $n$ 个大小为 $n-1$ 的集合,且每个集合的结构都相同,且足够简单。

一个自然的想法是,先找一个边集 $E$,使得 $n$ 个集合恰好是这个边集的旋转。具体定义如下:

把图中所有点标号为 $0,1,\dots ,n-1$。我们对于边集 $E$ 和整数 $x$ 定义加法:$E+x=\{(u+x\pmod n,v+x \pmod n):\forall(u,v)\in E\}$。也就相当于把图中的 $n$ 个点排成一圈并旋转这个边集。

问题2. 如果一个边集 $E$ 满足 $E+0,E+1,\dots ,E+n-1$ 恰好不重不漏地划分了图里的所有边,$E$ 的性质是什么?(充要条件?)

问题3. 最简单,性质最好的大小为 $n-1$ 的边集应该是哈密顿路径。构造一个满足上述条件的边集 $E$ 使得 $E$ 中的边组成一条有向哈密顿路径。

无向图也有类似的构造。有一个 $n$ 个点的无向完全图 $G$,它的边有 $\frac{n(n-1)}{2}$ 条。在很多构造问题中,我们需要做这样一件事:把这 $n(n-1)$ 条边根据 $n$ 的奇偶性分成 $n$ 个大小为 $\frac{n-1}{2}$ 的集合或者 $n-1$ 个大小为 $\frac{n}{2}$ 的集合,且每个集合的结构都相同,且足够简单。

问题4.1. 最简单,性质最好的大小为 $\frac{n-1}{2}$ 或者 $\frac{n}{2}$ 的无向边集应该是最大匹配。当 $n$ 为奇数时,构造一个边集 $E$ 满足 $E+0,E+1,\dots ,E+n-1$ 恰好不重不漏地划分了图里的所有边,使得 $E$ 是最大匹配。

问题4.2 当 $n$ 为偶数时存在一个边集 $E$ 满足 $E+0,E+1,\dots ,E+n-1$ 恰好不重不漏地划分了图里的所有边,使得 $E$ 是最大匹配吗?如果不能,尝试微调“旋转”的定义使得我们仍能将所有边划分成一组性质良好的最大匹配。(可以结合下面的实际应用思考)

现在我们来看实际应用。注意,“二元组互不相等”形式的约束通常可以图论建模。

问题5. 【QOJ5528】给定一个无向完全图,请你将它的所有边排成一个序列使得这个序列中每相邻 $n-1$ 条边都形成一棵生成树。(实际上,可以排成环形序列。)

问题6. 【P9837】https://www.luogu.com.cn/problem/P9837

问题7. [CF2081F]构造一个 $2n\times 2n$ 的拉丁方(每一行、每一列都是 $2n$ 的排列)满足每一对关于整个矩阵水平或垂直平分线轴对称的位置和都为 $2n+1$ - $a[i][j] = a[2n-i+1][2n-j+1] = 2n+1 - a[2n-i+1][j] = 2n+1 - a[i][2n-j+1]$ - 横着看相邻两个位置组成的有序对(共有 $2n \times (2n-1)$ 个对)全都互不相同 - 竖着看的有序对也互不相同

问题8.[bonus] 这个模式[pattern]可以称为zig-zag。你是否能想到类似的问题和拓展?

更多例题. 【P5419】

A036 我放弃了均摊数据结构

问题1. 小明听说 std::vector 能够维护一个数组,支持在末尾添加、删除元素和随意访问任意下标的元素。为了随意(也称随机)访问,std::vector 将元素存储在一段连续的内存中。

但是,计算机每次只允许申请固定长度的连续内存。我们认为申请一段固定长度的连续内存,无论长度多少都是 $O(1)$ 的。(仅申请,后续修改另外算时间。)

因此 std::vector 的实现方式是,有一个容量值 $C$,添加新元素时一旦超出容量,就将 $C$ 变为原来的 $2$ 倍,申请一段新的长为 $C$ 的内存,并把原来的元素复制过去,释放原来的内存。这个步骤称为重构。

小明想到了几个问题:

问题1.1. 证明 std::vector 在 $n$ 次操作的总时间复杂度是 $O(n)$ 的。

问题1.2. 小明发现如果添加很多元素之后删掉大部分元素,vector 内的的元素个数会远远小于容量,这很浪费内存。因此小明希望在元素太少时也重构,使得 $C$ 总是不超过当前元素个数的渐进线性。

小明仿照上述方法,规定在元素个数小于 $1/2 C$ 的时候就将 $C$ 变为原来的 $1/2$,申请一段新的长为 $C$ 的内存,并把原来的元素复制过去,释放原来的内存。

这种情况下,小明的 vector 在 $n$ 次操作的总时间复杂度是多少?

问题1.3. 做一些修改,实现问题1.2.中小明的想法,并且保证 $n$ 次操作的总时间复杂度是 $O(n)$ 的。

问题1.4. 小明发现虽然 $n$ 次操作的总时间复杂度是 $O(n)$ 的,但单次操作消耗的最大时间也是 $O(n)$ 的。小明希望减小单次操作的时间上界。有什么办法做到这一点吗?

提示:能否把重构均匀分配到之前的很多次操作里逐步进行,而不是在内存超过 $C$ 那一步一下子全部重构?

问题2. 小明听说路径压缩的并查集能够维护一些元素所属的集合并支持:1. 添加一个单点 2. 查询一个点所属集合 3. 合并两个点所在集合,但单次操作最坏是 $O(n)$ 的。

问题2.1. 现在有一棵 $O(n)$ 个点的并查集树(也就是说所有点都属于同一个集合,直接或间接连到同一个根),对每个点依次进行一次查询操作,没有合并操作。证明路径压缩的并查集在这种情况下的总时间复杂度是 $O(n)$ 的,无论查询顺序如何。

问题2.2. 小明发现某个并查集问题的操作有特殊性质:一共 $n$ 次操作,操作被分为 $k$ 轮:每一轮先进行 $a_i$ 次修改(操作3),再进行 $a_i$ 次查询(操作2),$2\sum a_i = n$。小明想要用路径压缩的并查集做这个问题,使得总时间复杂度仍然不超过 $O(n\log n)$ 的同时,每次询问的最坏时间复杂度(从读入这个询问到输出答案)都是不超过 $O(k)$ 的。

问题2.3. [经典问题,离线动态图连通性][P5787; LOJ121]有一个图,支持加边,删边,查询两点是否连通。一个经典做法是线段树分治,每条边的存在时间是若干个区间,这些区间共有 $O(n)$ 个。将这些区间放在一个以时间为下标的线段树上,对线段树进行遍历,进入/退出每个节点时执行/撤销这个线段树节点上区间对应的边(的连通性合并操作)。遍历到叶子时执行了的恰好就是这个时刻存在的所有边。

为了保证撤销的复杂度正确,一般用单次复杂度有保证的数据结构维护连通性,例如按秩合并的并查集。用路径压缩的并查集能不能解决这个问题?

问题2.4. 你能利用这种方式改进上述问题的时间复杂度吗? (参考答案-拓展阅读. https://www.luogu.me/article/asttym3k

AP006 前 k 小问题和最小扩展过程

问题1. 有两个长为 $n$ 的整数数列,问两个数列中各选一个数的所有方案中,第 $k$ 小的和是多少,$1\le k\le n^2$。请你设计出一个尽量高效的算法。

问题2. 有三个长为 $n$ 的整数数列,问三个数列中各选一个数的所有方案中,第 $k$ 小的和是多少,$1\le k\le n^3$。请你设计出一个尽量高效的算法。

提示:以下这个问题是困难的:

给定 $n$ 个值域较大(位数上界为 $\omega(\log n)$)的整数,问其中是否有三个数的和是 $0$。对于这个问题,人类目前没有找到任何时间复杂度在 $O(n^{1.999999})$ 的算法,也就是目前该问题时间复杂度的多项式次数不小于 $2$。这个问题被称为 3SUM

假如你有一个问题2的算法,你可能可以利用它设计出一个 3SUM 的算法,从而说明问题2的算法很难低于某个时间复杂度。

如果我们能说明在 $k$ 无约束的情况下问题是困难的,我们就知道了为什么总是考虑 $k$ 较小时求前 $k$ 小/前 $k$ 大,并且时间复杂度和 $k$ 有关。

问题3. 上次课程中已经给出了一个 $O(n+k\log k)$ 的问题2的算法,请你尝试总结该算法的适用范围和条件。你可以添加/删除条件看是否还能做。并解决这个问题:有 $n$ 个整数,请你求出前 $k$ 小的子集和。

问题4. [HDU5757]有 $n$ 个整数,每个整数被染成了黑白两种颜色,请你求出有偶数个黑色元素的子集中,前 $k$ 大的子集和分别是多少。

问题5. 有 $n$ 个整数,请你求出所有大小恰好为 $m$ 的子集中,前 $k$ 小的子集和。

问题6. [P2541]有 $n$ 个数列,每个数列长为 $m$,问每个数列中各选一个数的所有方案中,前 $k$ 小的和分别是多少。

问题7. 有 $n$ 个数列,每个数列长为 $m$,问每个数列中恰好各选 $c_i$ 个数的所有方案中,前 $k$ 小的和分别是多少。

并解决[P6646]。

问题8.[bonus] 根据以上问题,你能否改编或设计出一个需要用到类似方法的问题?

拓展阅读.[bonus] 一种不是保证从小到大扩展,而是类似迭代加深搜索的二分做法:https://www.luogu.com.cn/article/8lob9dt3

AP026 树上复杂度分析练习

问题1.1. 有一棵 $n$ 个点的有根树,每个点的权值等于自身的子树大小减去最大的孩子子树大小。所有点的权值和的上界估计是多少?

问题1.2. 有一棵 $n$ 个点的有根树,每个点的权值等于自身的子树大小减去前 $k$ 大的孩子子树大小。所有点的权值和的上界估计是多少?

问题2.1. 有 $n$ 个集合,每个集合初始大小为 $1$。你每次可以合并两个集合,设两个集合大小分别为 $a,b$,合并代价为 $\min(a,b)$,合并成一个大小为 $a+b$ 的新集合。所有集合最终合并成一个。分别估计最优合并顺序和最劣合并顺序对应的总代价。

问题2.2. 有 $n$ 个集合,每个集合初始大小为 $1$。你每次可以合并两个集合,设两个集合大小分别为 $a,b$,合并代价为 $a+b$,合并成一个大小为 $a+b$ 的新集合。所有集合最终合并成一个。分别估计最优合并顺序和最劣合并顺序对应的总代价。

问题3. 有 $n$ 棵字典树,且它们的点数总和为 $O(n)$,树的深度均不超过 $d$。合并两棵树的代价与两棵树交集的点数成正比,合并得到的为两个树的并集。所有树最终合并成一个,估计合并总代价的上界。如果把“点数总和为 $O(n)$”的限制加强成“字符串总长(所有叶子深度和)为 $O(n)$”,会有变化吗?

问题4. 有一棵 $n$ 个点的无根树,初始情况下每个点的颜色都是 $0$。$n$ 次操作,第 $i$ 次操作在树上选一条路径将路径上的点染成颜色 $i$。问:在操作过程中和结束后曾经出现过的不同的极大同色连通块种类数上界是多少?

问题5.1. 有 $n$ 个集合以及常数 $m$,每个集合初始大小为 $1$。你每次可以合并两个集合,设两个集合大小分别为 $a,b$,合并代价为 $O(a\cdot b)$,合并成一个大小为 $a+b$ 的新集合。所有集合最终合并成一个。分别估计最优合并顺序和最劣合并顺序对应的总代价。

问题5.2. 有 $n$ 个集合以及常数 $m$,每个集合初始大小为 $1$。你每次可以合并两个集合,设两个集合大小分别为 $a,b$,合并代价为 $O(\min(a,m)\cdot \min(b,m))$,合并成一个大小为 $\min(a+b,m)$ 的新集合。所有集合最终合并成一个。分别估计最优合并顺序和最劣合并顺序对应的总代价。

AP027 带权并查集练习

带权并查集:在并查集数据结构中,每个集合对应一棵有根树。我们可以在并查集树上维护每个点到根的信息,方法是维护每个点到并查集父亲的边信息,并在路径压缩时进行信息合并。

从集合的角度,可以认为带权并查集的权值是支持给某个集合整体加一个相同的值。

带权并查集通常用于维护原问题每个集合(连通块)内任取一点到其他点路径都足够使用的信息,例如二分图染色等。

带权并查集经常因为维护信息不能改变连边顺序,导致无法使用按秩合并。从给整个集合加操作的角度,这等价于操作的可逆性

问题1.1. [P1198]一个数列,要求支持向末尾插入一个数以及查询末尾 $L$ 个数的最大值,强制在线。请你用带权并查集解决这个问题。

问题1.2. 证明你在上个问题中的时间复杂度,并给出一个达到渐进上界的数据构造。(卡满你的算法时间)

问题2. [P2024] [NOI2001] 食物链。解决这个问题。另外,你做这个问题的并查集能同时使用路径压缩和按秩合并(启发式合并)达到 $O(n\alpha (n))$ 的时间复杂度吗?

问题3. [LOJ508弱化版] 你有一个 $n$ 个点的无向图和一个常数 $m$,有两种操作:添加一条连接 $(u,v)$ 权值 $w$ 的边,以及询问是否存在 $u$ 到 $v$ 的路径(可以是非简单路径)长度 $=x\pmod m$。

问题4. 类似离线 Tarjan LCA 算法的扩展,带权并查集可以用于计算树上路径信息。请你做这个问题:给定一棵 $n$ 个点的树,每个节点上有一个权值 $w_i$,请你求出给定的 $q$ 条路径形成的序列的最大子段和($n,q\le 10^6, w_i\le 10^9$)。

【欢迎投稿】有奖征集 OI 小知识点,思考题和科普,包括“广为人知”!

2025-11-07 18:38:48 By liu_cheng_ao

有奖征集 OI 小知识点,技巧和思考题,包括“广为人知”但大纲未收录的内容!

让所有重要的知识和技巧都“广为人知”,让所有偏门、鸡肋的知识和技巧都“物尽其用”。

投稿:发送至 oistars@qq.com 并同时回复本帖

本文在 LibreOJ 最新,不过所有平台均接受投稿!

核心规则

总述

有奖征集 OI 中大纲未收录的小知识,套路,技巧和思考题!包括你觉得“广为人知”或者不为人知大纲里没有的内容!

让所有重要的知识和技巧和你的优秀笔记都被推荐“广为人知”,让所有偏门、鸡肋的知识和技巧都作为思考题“物尽其用”。

征集范围为公开资料,可以直接推荐网络公开资料,也可进一步提供相关的笔记介绍、优质课件、例题、思考题等。撰写详细公开资料者以及发明/引入人有更多奖励

同一知识的推荐奖励先到先得。但对于详细公开资料作者,只要有新内容就有奖励,不受他人推荐影响。对于发明/引入人,奖励不受影响。

对于那些太细节/偏/杂/不实用的知识,可以造思考题。思考题分为 NOIP+ 和 NOI 两档难度,均有更高奖励。思考题比较类似大学算法课的思考题讨论,每个思考题围绕一个技巧/数学概念/动机等小套路展开,用于补足编程题不易表达的内容。思考题的例子可以参考:liu-cheng-ao.blog.uoj.ac/blog/9751

奖励内容为 现金+等额“星尘”(贡献分),会展示“星尘”前几名排行榜,可以选择匿名。多次贡献的同学可以加入在线文档和群方便查重交流。

  • 知识公开介绍推荐:最高 160 每个。
  • 知识公开介绍作者:最高 320 每个。
  • 思考题作者:最高 1024 每个。
  • 知识发明/引入者:最高 2048 每个。

鼓励投稿内容的投稿者自行公开传播其知识和文章。 去其它地方有偿投稿相同内容(有偿一稿多投)须事先明确告知双方并取得同意。

收集的知识列表会公开展示(审核、整理、展示等可能有延迟)。

收集的投稿内容会用在教材,题单,资源推荐等类型的公开项目同时也会用于训练课程

联系方式

  1. 同时详细内容/链接等及您的个人身份发送到投稿邮箱
    • 【邮箱:oistars@qq.com】 (旧邮箱 1525887403@qq.com 仍有效,但不建议发新内容)。
    • 您的网名,用于贡献榜展示,可以注明匿名。
    • 您的 OIerDB 个人页面,如果没有,可以写真实姓名+出生年份。用于发奖励。
    • 您的一个 QQ号,以便验证身份和后续沟通。
    • 您的个人信息不会被公开或传播给他人,除了贡献榜展示的网名外。
    • 您可以不提供个人信息,但没有信息将无法发奖励。
    • 即使您已经回复了本帖评论区也要发邮件。投稿时间以邮件首次发送完整内容的时间为准,细节可以后续勘误补充。
  2. 直接将小知识推荐和你的介绍/例题链接回复到本帖评论区。
  3. 我们会用邮箱回复您的投稿。
  4. 可以加入蔡德仁的 OI 教研群催促鸽子审核:群 QQ 435253885
  5. “星尘”(贡献分)达到 300 可加入征集讨论群 QQ 1061507046以及在线文档,便于讨论。

征集内容

在 OI 中的小知识,即常说的套路、技巧[tricks],和相关的笔记介绍、例题、思考题等。

  • 作为核心难点能出 $\ge 2$ 道不同 OI 题目的算法技巧、组合结构、数学模型、经典结论、算法方法等各种均为征集范围。
  • 不能是 NOI 大纲或大纲辞典明确收录过的内容。
  • 必须 OI 可做,不能过于偏/难,至少集训队得有几个人会或能立刻学会。
  • NOI 及以下难度的优先

征集形式

一、小知识推荐

  • 必须提供:较详细的介绍;或公开的介绍博客/资料链接。可以推荐其它人写的内容!
  • 可选提供:例题(需提供 题目来源链接或题意+题解)
  • 奖励:单个知识最高 160
    • 64(基础介绍)+32×(每道不同例题
  • 已有人推荐过的知识不计奖励,新例题仍计先到先得

二、详细笔记

  • 必须提供:本人撰写的一篇较详细的公开介绍博客/资料/课件,必须包含知识的详细介绍、思想、典型应用以及至少一道例题及题解。
  • 或者:本人创作的 OI 知识相关优质大众科普视频/文章
  • 奖励:单个知识最高 320
    • 基础 160。如果是第一个投稿该知识的详细笔记,则奖励至 320
  • 可以投稿在本活动开始之前创作的内容,但必须是本人撰写。
  • 重复知识,必须包含相对之前投稿的新内容。先到先得

三、思考题

  • 对于那些太细节/偏/杂/不实用的套路,可以用来造思考题。
  • 必须提供:思考题的详细题面和解答,以及核心知识。
  • 奖励:NOI难度 1024,NOIP+ 难度 512
  • 建议造之前先说思路,防止撞前面的题。和之前的思考题知识重复但仍有新内容的,奖励 256先到先得
  • 思考题的例子可以参考:liu-cheng-ao.blog.uoj.ac/blog/9751

四、发明/引入人奖励(特殊)

  • 必须提供:该知识的详细介绍资料/链接。以及本人作为该知识发明人/OI 首先引入人的身份和初期资料。
  • 奖励:NOI 及以下难度 2048,更高难度 1280
  • 发明/引入人奖励对大纲/经典教材收录的内容仍然有效

“星尘”(贡献分)

1奖励 = ¥1 + 1“星尘”(贡献分)

想要多参与的同学,“星尘”(贡献分)达到 300 后,可以加入在线文档和讨论群,快速查重交流。

其它

  • 如果你想推荐具体代码写法,请转代码细节征集
  • 如果你想推荐比赛注意事项,请转比赛注意事项
  • 如果你想推荐小资源,请转小资源合集
  • 如果你想推荐其它公开资源,请转网络资源推荐
  • 这些其它资源如果对 OIer 较有用,且您撰写了高质量教程,也可以同时邮件投稿获得 160 左右的奖励。邮件投稿时别忘了去对应的收集帖回复。

细则

本活动规则的最终解释权归主办方所有。

  • 在 OI 中的小知识,即常说的套路、技巧[tricks],和相关的笔记介绍、例题、思考题想法等。具体范围如下:
    • 作为核心难点能出 $\ge 2$ 道不同 OI 题目的算法技巧、组合结构、数学模型、经典结论、算法方法等各种均为征集范围。
      • 例子:bfs求常数边权最短路;贪心的邻项合并;单侧递归线段树;min-max 容斥;颜色段均摊;析合树;斜二进制倍增;分散层叠的多序列二分;01原理的排序分析;下标-值域-时间换维在数据结构上的应用;均摊分步化;折线图法分析括号序列和01序列;压位分块(四毛子在特定问题上的实现优化);Bostan-Mori;最优化推式子技巧(例如 $\max(\lvert x\rvert)=\max(\max(x,-x))$);完全图zig-zag构造技巧;基于 Farey 序列的 $O(1)$ 在线查询逆元;矩阵乘法的常见 OI 归约;……
      • 下至 CSP-J 上至 NOI+ 难度都可以!
      • 这些已列出的例子如果你能提供好的例题,仍然有奖!
    • 不能是《NOI 大纲》《NOI 大纲辞典》明确收录过的内容
      • 你也可以投稿教材目录提到过的内容!
      • 例外情况:如果是作者/引入者本人领奖,不受限制。
    • 必须 OI 可做:
      • 必须是算法/计算机内容且能出 OI 题。
      • 不能是 OI 未引入过的偏僻论文、科技。(大致标准:假设给现役集训队统一讲解该知识 5 分钟,然后出 OI 题单独作为集训队测试,能有 4 人以上在 5 小时内 AC。)
      • 优先征集 NOI 难度以内可考的知识。
  • 奖励:
    • 一、小知识推荐
      • 之前有人推荐过的知识不计奖励。
      • 之前有人推荐过该知识,但你提供了新的例题的,只计算新的例题奖励。题意足够类似的例题算一道。仍然受单个知识上限限制。
    • 二、详细笔记
      • 可以投稿在本活动开始之前创作的内容,但必须是本人撰写。
      • 如果是重复知识投稿,必须包含相对之前投稿的新内容才计算奖励,如更易懂的解释,新的理解视角,或新的例题。
      • 同一人只能对同一知识奖励一次。
    • 三、思考题
      • 同一人只能对同一知识奖励一次。
    • 四、发明/引入人奖励
      • 发明人和引入人是不同人的,独立计算奖励。
      • 发明人或引入人是多人共同的,共同计算奖励。
      • 同一人只能对同一知识奖励一次。

个人著作权声明:严禁任何未经本人(刘承奥,常用笔名/网名:蔡德仁 CommonAnts LCA liu_cheng_ao)书面授权者在梦熊联盟,或者任何虚假宣传或不实营销炒作或不正当竞争行为严重的 OI 机构的课程内或交流平台(包括但不限于品牌集训线下讨论,交流群,OJ,公众号,视频号等)上引用、传播、讨论此内容,以及本人于2024年5月及之后发布的所有内容,包括声明为公开的内容在内。

之前已发布的本文草稿内容:https://www.luogu.me/paste/gzsaf4gu

2024年及更早的本人同类计划:https://pqsyrcnk3fo.feishu.cn/wiki/Xk79wgM8uiBQGRk6x6TcueXznaf

坚决反对打着“公共资源”旗号鼓吹“稀缺性”,妄图商业垄断的行为!

坚决反对打着“公共资源”旗号进行商业炒作营销,剽窃公开资源开设付费课程坑骗家长和学生的行为!

坚决反对与虎谋皮,以营销炒作的方式“推广知识”,破坏社区秩序的行为!

坚决反对通过向部分不懂算法的家长/教练危言耸听,吹嘘特定知识重要性,强迫学生学习,破坏教学秩序的行为!

坚决支持建设高质量公开资料推荐平台和刊物平台!

【欢迎投稿】OI 教学研究当前的若干具体问题:行动起来!

2025-11-04 14:39:31 By liu_cheng_ao

个人著作权声明:严禁任何未经本人(刘承奥,常用笔名/网名:蔡德仁 CommonAnts LCA liu_cheng_ao)书面授权者在梦熊联盟,或者任何虚假宣传或不实营销炒作或不正当竞争行为严重的 OI 机构的课程内或交流平台(包括但不限于品牌集训线下讨论,交流群,OJ,公众号,视频号等)上引用、传播、讨论此内容,以及本人于2024年5月及之后发布的所有内容,包括声明为公开的内容在内。

如果您想提出问题或建议,敬请留言!

OI 教学研究当前的若干具体问题:行动起来!

以下分章节列出笔者了解的OI教学研究亟待解决的,当前的若干具体问题。每个问题分为当前背景,问题概括,笔者个人思路(抛砖引玉)几部分。

教学资源问题

公开资源推荐/摘要问题

公开资源是 OI 最好的成文资源。 基本上,OI 的付费/私有资源仅有高水平同学(教师同理)和教学管理是公开资源无法代替的。对于题单模拟赛等情况,公开资源经筛选后的质量普遍比私有资源更高。但是公开资源缺乏整理。

问:公开资源缺乏整理,如何整理一些平均质量高,总量精简的公开资源供教学/自学使用?

个人思路(抛砖引玉,下同): - 由笔者等全职/半全职的高水平教研人员进行一个长期示范性的筛选项目。 - 鼓励广大 OIer 进行自己的筛选。 - 鼓励广大 OIer 进行推荐投稿。 - 对广大 OIer 的工作进行物质上和社区上的激励。 - 可以分类为入门/家长,普及,提高,进阶四个组。每个组的标准和宣传策略不同,但应该互通。 - 总的原则应该是少量高精,适合自学。 - 很多教练特别是经验丰富的老教练也做了工作,应该特别注意前辈的贡献。尤其是在入门阶段!

教材问题

缺少主干教程/教材。它应该是可自学的。刘汝佳,CCF中学生计算机程序设计系列,一本通系列等教材较过时。具体来说: - 入门:笔者教学水平和对入门学生的认知不够评价这个的,不过有不少教师研究,成果尚可。缺乏筛选和宣传推广。 - 普及:洛谷的书尚可 - 提高:洛谷的书和李老师《算法竞赛进阶指南》尚可 - 进阶:空白 (笔者当前主要工作) - 数学前置知识:缺乏 有一些教练编的书,但水平有限

问:入门和普及书缺乏筛选和宣传推广,进阶教材空白,数学前置知识教材缺乏,怎么做?

个人思路: - 笔者主编一套进阶教材书,涵盖进阶知识中所有基本方法和大知识点/常见技巧,并兼顾某些金牌选手继续对接学术界的视角需求。 - 鼓励广大 OIer 多写算法技巧和数学前置知识笔记总结,并收集编修推荐。 - 对广大 OIer 的工作进行物质上和社区上的激励。

社区问题

社区组织问题

有很多 OIer 对 OI 社区本身感到迷茫,这阻碍了分享和交流。OI 社区需要一面旗帜来作为坚强的希望。

问:如何让 OI 社区更加组织起来?

个人思路 - 首先,OI 社区要组织起来的办法有且仅有一个,那就是在不断解决 OIer 自己的具体问题的过程中达到。 - 不是整个 OI 社区需要被组织,而是社区中希望做一些工作的 OIer 会想要一个组织,学习生活中感到孤独的 OIer 会呼唤一个组织。 - 需要有人在清华北大/集训/强校附近等 OI 社区核心人员集中区域进行联络,维持 OI 社区的讨论,并且通过游戏、茶话会等方式联系大家。笔者预计在每年 3 到 6 月的春季学期常驻海淀。 - 可以先推进教材、公开资源推荐、刊物等同学们最关注最需要的事情,达到一个阶段。 - 需要研究出如何有效地进行物质上和社区上的激励。光是发钱不行。 - OI 有没有类似“街坊邻里送锦旗”那种有效的群众之间能自发进行的夸人方式?(也许发展一下徽章交换?) - 发展 OI 民间公开奖项!应该搞几个年度好题奖,年度好文奖等。 可以以各大公开 OJ 管理组、刊物编辑部为核心结合群众投票来搞。可以背靠清北等大学进行提升获得感。 - 能不能让 CCF 或者大学搞点激励? - 包括一些乱象和乱搞的机构在内,外部压力也会促使 OIer 不得不在某种程度上团结起来。

报刊问题

洛谷日报停办后,包括洛谷专栏全站推荐在内的报刊类平台流量较低,起不到真正加强交流,推广宣传优质资源的作用。最近还发生了有 OIer 被逼到营销炒作的闹剧。应当解决这一需求。

问:如何办一个报刊,加强 OIer 的交流并推广宣传优质资源?

个人思路: - 笔者联系组织一个电子周刊,初期内容类似升级版本的洛谷日报,分为多个栏目。初期内容可以少,乃至不同期轮换不同栏目,但一定要以周刊频率更新并保持宣传。 - 接受投稿并且主动联系具体 OIer 个人征稿。 - 可能的栏目: - 知识/技巧讲解 - 好题推荐和题解 - 杂谈杂文 - 入门/趣味科普(非常重要!) - 教师讲谈(可以请一些关心 OI 发展的年轻教授等) - 公开项目和高质量资源推荐页(每期必备) - 独立广告页(筹集经费和团结正常机构学校等)(每期必备) (广告页不属于正文,而是单独的图感谢支持。向家长,教练等推送时由推送者个人附带发送。) - 其它……(请大家集思广益提建议) - 每一期在洛谷、UOJ、QOJ、LOJ 等大公开 OJ 全文发送,并允许正常机构转载。 - 直接推送给各大 OJ 群和网上活跃的选手。 - 直接推送给各大教练和学校。 - 大量制作切片、梗图等在家长群、公众号、自媒体等一切平台大力传播,扩大影响。 - 为了筹集经费和团结正常机构学校等,必须有广告页,但必须保留独立性和每期的接收权。拒绝一切劣质机构。 - 应该进行大项的财务公开,但不要被舆论绑架去公开不适宜内容。 - 通过 CCF/科协和清华北大及其中个人等,获取合规资源和名号(最好有网版号,或 NOI 冠名等)。当然,具体内容事务都要由 OIer 负责。

鸽子复活/鸽子起飞问题

OI 经常有同学搞很多好的项目,但随着创始人的忙碌升学等很多都渐渐废弃。应该恢复和发展这些现成的好资源,达到事半功倍的效果。例如 OI-Wiki,Public Judge,退役OIer文化课交流群等。除了鸽子,也有很多萌芽状态的好项目类同需要支持。

问:如何恢复和发展这些鸽子项目/让新鸽子起飞?

个人思路: - 这些项目主要是缺乏人力,而且技术要求极高,必须恢复核心人员或找到后继。需要调查研究每个项目核心成员的具体情况。 - 根据一些了解,OIer 对此类资源普遍的抽象讨论和跟风差评等造成了严重影响。做社区心理建设时应该特别做好拥护高质量公开资源的工作,也应该在 OI 建设过程中让 OIer 达到团结。 - 也许可以给资助? - 也许可以和大学合作挂点项目? - 也许可以有人去清北或者某些 OI 社区核心人员聚集地(尤其是高中生的)经常联络,推大家干活并且组织一些欢乐活动?对于一些强校,可以考虑在当地找一些成熟的学生或者教练负责。 - 很多教练特别是经验丰富的老教练也做了工作,应该特别注意前辈的贡献。推广和改进它们的质量。尤其是在入门阶段!

讨论群摘要问题

OI 有很多高质量资源的来源是讨论群,比如 UOJ 群消息记录。应该搞一些 AI 消息摘要总结出其中有价值的内容。

问:如何做讨论群消息摘要?

个人思路: - 已经有人干了,支持他们。类比鸽子起飞问题。

提问平台问题

OI 是不是需要一个 OI StackExchange?优点是便于搜索问题和提问。

问:OI 是不是需要一个 OI StackExchange?

个人思路: - 目前来看没有其它问题紧急,也许周刊办起来之后会看到究竟是否必要。 - StackExchange 类讨论平台需要的社区人数远大于讨论群,特别是需要某种专业态度,OI 难以支撑。 - OI 更合适的还是比较市井的讨论群:群灌水无法避免,只是对我们来说讨论问题是灌水的一部分。选手水平高了自然也就讨论得了更难更深入的内容。 - 因此个人认为上面的讨论群(AI 摘要弥补不能积累、搜索的短板),加上报刊,以及 OJ 讨论区,比开 StackExchange 更好。后者看不到维持方法。

应用开发问题

原题机问题

所谓原题机就是给出题意,搜索推荐原题和高相似度题的搜索引擎。现在已经有了几个基于 LLM 的原题机项目,比如 yuantiji 和 CPRet。但现在只能做到搜题面相似的,模型能力和微调都较差,也没有利用好问题的题解,标签,程序等数据。还做不到搜想法或简要题意等。

问:如何推动原题机开发?

个人想法: - 在鸽子问题的基础上帮助现有项目继续推进,需要经费。但应该不多,百万级别即可。其它事情初具规模后可以资助此类项目。

造题平台问题

目前有一些造题平台。比如 polygon,tuack,乃至较新的 hull 等。但是他们技术上虽然足够使用,可是学习门槛高用户体验较差,不太适配一般用户和教练之类的使用需求。用户体验不好就没法以程序自然而然培养用户习惯,从而推广大样例等规范和降低错误。

问:如何开发一个高用户体验的造题平台?

个人思路: - 在鸽子问题的基础上帮助现有项目继续推进,需要少量经费。十万级别即可。另外还需要一个懂用户体验且懂 OI 的产品经理,这个暂且不知道。

AI 辅助教学问题

随着 AI(目前主要是 LLM 路线)的能力发展,应当且必须研究开发 AI 在教学中的应用。不能指望大机构,商业决定了他们的开发主要是 AI 读稿降本增效,而不关心实际效果。

问:现在应该开发哪些具体的 AI 辅助教学方式?

个人思路: - 应该推动 AI 资料查找在教学中的使用。 - 应该推动 AI 思维链在教学中的使用。 - 应该推动 AI 辅助编程/调试的实用化,但不能整体替代,最好达到学生平时写代码框架用 AI 辅助完成机械工作,模拟赛完整自己写的状态。 - 应该在现有大型 OJ 中引入高质量的 AI 推荐等系统。 - 应该按照鸽子问题支持已经存在的这类项目。

OI 和 OIer 发展问题

社区心理建设问题

OIer 在平时孤独学OI的迷茫中积累了大量心理压力,并且承担着巨大升学风险、不被认可的社会压力等。抽象卖弱等是普遍情况。如何对待?

问:如何进行社区心理建设?

个人思路: - 充分尊重 OIer 压力巨大的现状。将外部压力分为心理环境的困难(比如 OI 受歧视,OI 社区搞抽象卖弱带来的压力)和硬困难(比如个人家庭问题或升学压力)两个部分进行考虑。 - 在 OI 社区中建立机制降低 OI 社区本身带来的问题,尤其是减少 OIer 向其它 OIer 发泄形成的恶性循环。(参考上面的社区组织问题。) - 对于硬困难,一方面减少困难,一方面组织互助(比如退役OIer文化课),一方面介绍社会具体情况,并找前辈进行交流,打破神秘幻想。对于个别事件进行具体帮助。 - 用成文的娱乐代替碎片的娱乐。不要完全禁止打游戏,但坚定推进游戏和聊天代替短视频,发展多人游戏和线下游戏/运动。 - 坚决支持和帮助学校教练这一重要力量。 - 向家长传播现状事实和正确观念有利于家庭和谐。

集训队发展问题

集训队选手脱离 OI 的一部分中,有迷茫不知所为的问题。留在 OI 的一部分中,有继续学习算法/计算机和对接学术界的发展问题。总的来看,问题是周围人不能真正的理解集训队选手的情况,没有专业的讨论和指导,看不到未来的道路。尤其是初中集训队水平选手情况极其严重。“上午卖弱,中午颓废,下午鱼鱼,晚上抽象。”成为普遍情况。

问:如何推进集训队发展?

个人思路: - 对于高二学生,继续建设大学预科。 - 对于低年级学生,作为前辈多交流,并且可以找关心 OIer 发展、专业水平过硬的大学教师/从业人员等,多组织对话研讨等活动,破除对未来的无知状态。这一对话形式也可以有较为普及化的版本,见报刊问题。 - 可以发扬 UOJ 群这样前辈较多的交流作用,并发展上述交流人员的讨论社区(群?)。 - 适当找事干,例如跟前辈学习某些学科分支、建设 OI 社区、其它计算机兴趣、发展个人爱好等。 - 青少年的迷茫和不知道做什么是正常情况,应当在参与各种具体的学科分支和具体接触各种事物过程中考虑。不要太急,“玩”两年绝不是浪费时间! - 坚决反对拿低年级学生炒作甚至交易的行为!

退役OIer文化课自学组织和自学资源开发问题

OIer 退役后有学文化课(乃至大学学习以及自己想学习的内容)需求。应当发扬 OI 社区的优点,对文化课乃至一切学习人数多的科目都进行教研,分享公开资源建立社区。

问:如何推进中学文化课/大学的自学社区发展?

个人思路: - 学习方法是共通的,而且 OIer 的自学能力和理解能力普遍足够。OI 有组合模型、推导、算法技巧;文化课也有问题套路和二级结论。完全可以用类似方式分享公开资源,例如笔者公开资源推荐中的范例。 - 结合该领域原本的自学资源。例如大学计算机的 csdiy.wiki,文化课的笔袋(@一数)等。 - 如鸽子问题,推进退役 OIer 文化课交流群等的发展。 - 长期来看,OI 社区的好的自学组织经验输入其它领域,将会促进普遍教育的普惠发展。

OIer 的非 OI 计算机兴趣发展以及兄弟组织的协作问题

目前 OI 仍然承担着大量仅仅是对计算机感兴趣或者仅仅是不想学文化课的学生,处于转型时期。许多 OIer 有转向非 OI 的其他计算机专业方向的兴趣和需求。另外,这几年蓬勃发展的新高考信息、少儿编程、大学本科计算机课改、CTF、LLM竞赛等也都需要 OI 的经验支持。

问:如何推进 OIer 的非 OI 计算机兴趣发展?如何推进兄弟组织的协作?

个人思路: - 对于大学本科计算机课改、CTF、LLM竞赛等兄弟项目,应当鼓励有志的 OIer 在中学毕业后进行参与,并有意建立与 OI 的联系,用 OI 的经验支持之,与其中具体的学生和教师紧密团结。 - 对于新高考信息、少儿编程等基础教育普及活动,OIer 毕业生将是师资的中坚力量,应当在这些领域建立学习和自学的资源协作,并用 OI 的经验支持之,与其中具体的学生和教师紧密团结。 - 个人发展的其他方面参考集训队发展问题,主要还是找前辈破除未知状态。

教练学习问题

目前有许多教练实际上有提高自己教学水平的愿望和时间投入。如何让他们学习更有效?

问:如何让愿意提升教学水平的教练高效学习?

个人思路: - 学习方面是和学生类似的,但不需要写太多代码。 - 能够自行搜索的时间更少。因此,刊物和教材如果能够办成,有很大效果。 - 能否同时发展 CCF 的教师培训? - 个人想法较少,恳请各位集思广益。

科学传播和推广问题

科普问题

目前已经有不少 OIer 在视频网站等地做科普。怎么帮助他们做得更好?

问:如何帮助 OIer 的科普做得更好?如何推广传播?

个人思路: - 完整的科普较多发在个人博客或中长视频站等平台。 - 科普水平主要来源于教学水平,特别是教研和学习方法。因此提高 OI 水平的教研和学习方法就是主要途径。大家学会学习,学会做笔记就是学会讲。 - 可以同时鼓励一些非 OI,非算法内容。 - 可以在刊物中推广。可以办奖。参考社区问题。 - 要兼顾选择一些热门的具体入门问题进行科普。革新派科普的重点在于讲好一个简单和吸引人的问题,传播科学精神。不要贪大求难。 - 短平台传播参见入门与家长传播问题。

入门和家长传播问题

目前家长中,尤其是入门级别(含少儿编程),充斥着大量营销号和垃圾信息。缺乏好的科普和入门资料以及传播。

问:入门和家长传播问题怎么办?

个人思路: - 制作一些精简而专业的入门资料(例如学习阶段、考试注意事项、热门问题讲解等),以一图流、切片等方式,在学校、家长群、公众号、短视频、图文等一切平台猛烈地传播,并且附加公开资源以及有高质量课程的地址。(这是必要的,因为必须解决家长需求。) - 不要怕丢脸,不要怕被骂,到群众中去。不要有顾虑,不要因为不敢带水印、不敢带课程链接、不敢蹭热度、不敢碰瓷指出错误,束手束脚反而忽视家长的实际需求。只要有立锥之地,即使一开始只有百分之一的家长相信,营销号就不能为所欲为,他们为了流量也就不排斥讲些真话。 - 宣传稳定后,可以由部分可靠人员办一些超低价基础课程,通过极大扩张人数的方式降低价格。这类课程实际类似录播课,质量受限制,可以倒逼各个机构“产业升级”做高质量课程。OI 社区本身应该仍然保持严格的完全公开和非商业,不能有商业关系,不能自己办,以免遇到洛谷“不完全商业化”的挫折,以及避免被腐化的风险。 - 参见报刊问题。

中小学推广问题

目前很多学校有搞 OI 的需求,主要是新开始或者 NOIP+ 的需求。学校需要包装完整简单的解决方案,以及政绩,和经办人员的个人利益。应当和劣质机构竞争学校。

问:OI 社区可以怎样向学校推广优质资源,打击劣质资源和不正当竞争?

个人思路: - 向各个学校和教练直接推送高质量公开资源,例如周刊。 - 对公开资源进行完整包装,傻瓜式使用。公开资源开发也要考虑用户体验。 - OI 社区包括刊物等不参与直接的商业行为,通过分散公平地接受多家正常机构广告,忽视劣质机构,引导有需求的学校选择业务。OI 社区本身应该仍然保持严格的完全公开和非商业,不能有商业关系,不能自己办,以免遇到洛谷“不完全商业化”的挫折,以及避免被腐化的风险。

个人项目地址

结语

OI 作为算法和计算机科学的自学社区,OIer 作为自我发展的群体,已经在过去的历史中创造了:不长期依赖高校教授等外部教育资源,二十年做出最好的算法与计算机基础教育、本科教育和基础研究生教育的人间奇迹。这是同学们的胜利,是 OI 社区的胜利,是竞赛这一自学组织政策的胜利。

现在,OI 已经发展到不再能从学术界获取现成答案,必须进行教学研究的阶段。近几年以来,广大 OIer 无私奉献,分享知识,以科学精神反思研究学习过程,拥抱新技术,产出了大量教学研究成果,变教学无用为教学有用,变资源壁垒为资源共享,使 OI 取得了现在的伟大进步。

现在,从少儿编程到中学课堂,从集训机房到 OI 赛场,营销猖獗,欺骗泛滥。OI 不可能在浪潮中独善其身,必须主动拥抱广大入门学生及家长的需求,才有可能为自己争得良好的环境。

现在,同学们有了各种各样新的需求。现在,OIer 不仅包括 OI 社区讨论核心,同样包括入门学生,退役选手,关心教学的教练。乃至是大学教师和 NOI 科学委员会中一切推动 OI 的高质量、普惠学习的人

我们应当自觉起来,我们应当立刻动手解决自己的问题,我们应当响应同学们和家长们对我们的期许和召唤。

我们应当响应政策的号召,利用好一切有利的环境,发挥我们在大中小学的人和资源,包括 CCF 中具体的助力,在保持公开和独立的同时团结广大合理运营的机构。

我们应当利用切片、公众号、自媒体等一切新的手段,使用学校、教授和 CCF 等一切可以获得的支持,向广大(特别是入门和少儿编程)家长和学生广泛公开地宣传高质量的学习资源,正确的学习方法和 OI 乃至整个少儿编程和大中小学计算机教育的真实现状。不要怕丢脸,不要怕被骂,到群众中去。只要有立锥之地,即使一开始只有百分之一的家长相信,营销号就不能为所欲为,他们为了流量也就不排斥讲些真话。

对于合理运营的机构和其他组织,我们应当在保持自身独立性的同时与他们中愿意提升具体教学质量,宣传正确观念,不搞不实营销炒作和不正当竞争的项目、人员及部门紧密合作。促使他们在机构内的权力巩固,压制传销派别。

对于那些严重虚假宣传,严重不实营销炒作,严重不正当竞争,严重欺骗家长学生和学校,严重破坏 OI 社区的机构和行为,不仅要坚决彻底地揭露和反对,还应该向他们的受害者宣传和提供真正优质的公开资源。这些在金钱面前胆小怕事的人,就不得不有所收敛,不得不讲些真话了。

对于升学或竞赛政策的推进等,我们从具体的事做起。能动的具体问题要动,能说话的机会要说。对于具体学生,一方面是指明中学毕业后未来的发展道路,协助 OIer 了解社会的具体情况,联系认识相关人员,破除所谓“社会”的迷雾和神秘的幻想。另外,对中学生特别重要的一点,是在文化课等学习发扬 OI 的长处。OI 可以总结推导和技巧,文化课没有道理不能总结方法和二级结论,并以 OI 社区的方式产生公开资源进行互助。

向科学和普惠的教育前进!

如果您想提出问题或建议,敬请留言!

【欢迎投稿】一文概括所有比赛注意事项,以及同类资料推荐

2025-10-31 22:04:47 By liu_cheng_ao

大部分文章在我的 LibreOJ更新,其他平台可能更新不及时,但你仍可以在其他平台投稿征集内容和建议。

个人著作权声明:严禁任何未经本人(刘承奥,常用笔名/网名:蔡德仁 CommonAnts LCA liu_cheng_ao)书面授权者在梦熊联盟,或者任何虚假宣传或不实营销炒作或不正当竞争行为严重的 OI 机构的课程内或交流平台(包括但不限于品牌集训线下讨论,交流群,OJ,公众号,视频号等)上引用、传播、讨论此内容,以及本人于2024年5月及之后发布的所有内容,包括声明为公开的内容在内。

使用这些建议时请确保你理解其内容,由于使用这些建议造成的问题与本文作者无关。

如果你有自己熟悉的代码习惯,考前不要变更自己的代码习惯!这个优先级高于一切!可以考完后花一个月慢慢适应好的写法。本文也不是唯一答案,如果你习惯你自己的策略,那就不要改,参考一下细节即可。

省流版

包含下述所有内容的摘要:

  • 在平时模拟赛就学会并习惯比赛策略,命令行用法,对拍等内容!
  • 考试须知 身份证,准考证,别迟到,收手机,考场纪律啥的。请看:官方提供的考试须知。
  • 比赛策略
    • 前 15% 时间读所有题并想所有题,然后开始写最高的分以及调试,最后 5 分钟检查
    • 看题一定要逐词看过去,包括封面和提示。读完题先手算小样例确认理解。
    • 先看完所有东西,再想过所有东西,最后写东西。
    • 每几分钟(或喝水,伸懒腰)提醒自己是否发呆,发呆立刻切换
    • 先写得分性价比高的,性价比相近时先写短的(比如暴力)。
    • 先写程序框架,每段大致含义,关键内容的原问题精确数学意义,后填代码。
    • 有时间要对拍
    • 要写部分分
    • 别怀疑别人比自己强/自己是弱校弱省/做不出题怎么办,至少你看的这个指南是最强的老师写的不是吗?
  • 编程注意事项
    • 与其去背易错细节害怕记漏,不如养成好习惯消除错误风险!
    • 缺头文件 问:头文件打少了爆零怎么办?
      • 答:#include <bits/stdc++.h>
    • 命名冲突 问:自建变量 y1 next pipe 等和标准库重名爆零怎么办?
      • 答:namespace 包裹自己的程序
    • 少用宏 问:#define 让我爆零怎么办?
      • 答:非必要不用宏,用 using 和函数等代替。
    • 编译器帮你查错 问:函数没返回值/隐式类型转换出错/重名变量等查不出怎么办?
      • 答:添加编译选项 -Wall -Wextra -Wconversion -Wshadow 让编译器帮你检查。
    • 调试工具 问:有什么工具帮我查运行错误/下标越界/数值溢出?
      • 答:调试时按需求添加编译选项 -g3 -fsanitize=address,undefined -D_GLIBCXX_DEBUG 等并使用对应的 gdb asan 等工具。
    • 忘改调试/注释 问:调试的时候注释了 freopen 忘记恢复/忘删调试语句爆零?
      • 答:采用宏 #ifdef MYDEBUG,不要注释。
    • 输入输出优化 问:用什么输入输出?要读入优化吗?
      • 答:平时用关同步 cin/cout,量巨大时用 cin.read() cin.gcount()/cout.write() cout.flush()
    • 开栈空间 问:我本地测试怎么开大栈空间?
      • 答:Linux(含 NOILinux)在终端运行程序前先运行指令 ulimit -s 1000000,Windows 编译选项添加 -Wl,--stack=1000000000
    • 测时间和内存 问:如何测自己程序时间和内存?
      • 答:Linux 用 time ./程序名 测时间,ulimit -v <内存限制KB数> 测超内存限制。Windows 用程序内 clock() 测时间,用命令行 cmdsize 测内存。
    • 考试环境是 Windows 问:考试环境是 Windows 怎么办?还有前面没提到的吗?
      • 答:务必打开显示文件扩展名。
    • 不要写超标语法 问:用超标/被禁语法爆零了怎么办?
      • 答:平时就别写超过 -std=c++14#pragma 之类的违禁品。

防爆零指南之:考试须知和考前准备

  • 在平时模拟赛就学会并习惯比赛策略,命令行用法,对拍等内容! 临时抱佛脚可翻到本文最后一部分的推荐文章。
  • 身份证,准考证,别迟到,收手机,考场纪律啥的。请看:官方提供的考试须知,以及考试现场通知。
  • 试机一定要试机器,有问题找监考不要自己修电脑,监考水平差不要惊慌,不要顶撞。试机器特别要注意键盘鼠标功能,开关机不还原的区域等。Windows 的话还得注意找编译器路径考试的时候设置环境变量方便 cmd

防爆零指南之:比赛策略(如果你有自己熟悉的习惯和策略,那就不要改!)

要点:

  • 前 15% 时间读所有题并想所有题,然后开始写最高的分以及调试,最后 5 分钟检查。 如果你没有一个自己的非常明确的策略建议这个。检查包括关闭代码编辑器,用题面说的编译选项编译代码,测试样例输入输出正确(特别注意不能有调试输出),检查文件名、文件夹格式等,检查期间和之后不要打开代码文件以免误触。
  • 看题一定要逐词看过去,包括封面和提示。读完题先手算小样例确认理解。
  • 先看完所有东西,再想过所有东西,最后写东西。 不要先写第一题后看第三题。读题的时候除了你能立刻流畅想下去的做法继续想,一旦卡一点就记下来,不要强行想。先看完后面的题,对所有东西有个整体把握。同理,开局第一小时还有题没想过的情况下,会做题了也不要急着写,先记下程序框架和关键变量的意义,然后先想想其他题。
  • 每几分钟(或喝水,伸懒腰)提醒自己是否发呆,发呆立刻切换。(上厕所调整,换大思路,写其他题等)避免浪费时间。不要一直死磕想/调一个题。
  • 先写得分性价比高的,性价比相近时先写短的(比如暴力)。 得分性价比是脑内大致估测的得分除以预计写代码时间,别花时间估计,有个印象就行。
  • 先写程序框架,每段大致含义,关键内容的原问题精确数学意义,后填代码。 可以用注释写。内容含变量、数组、类型、函数等,要精确到 $f[i]$ 到底是考虑了 $\le i$ 还是 $\lt i$ 的状态。这样可以解决自以为会了但没想清楚的,调试的时候也可以逐段对比反例找到错点。
  • 有时间要对拍。 另外写的暴力也能帮你得部分分,打表找性质等。有时间就枚举所有小数据(能测边界情况)+测试极端数据。没时间对拍就手算几组小数据和小边界。
  • 要写部分分。 时间多又不会写更高分,且对拍写完时,尝试写乱搞。

详情请看课件:《如何打比赛.pptx》《编程基础:设计,测试,调试.pptx》。(我目前没地方放公开文件,暂且可以加我的QQ群 435253885。UOJ 群也发了如何打比赛。欢迎各大 OI 群(除了声明中的机构群)和各位老师同学转载。)

防爆零指南之:编程注意事项(如果你有自己熟悉的习惯和策略,那就不要改!)

与其去背易错细节害怕记漏,不如用好习惯消除错误风险!

缺头文件

问:头文件打少了爆零怎么办?

答:#include <bits/stdc++.h>

详情:竞赛官方使用的编译器 GNU-GCC-G++ 提供的 <bits/stdc++.h> 包含了 C++ 所有的常用标准头文件(除了竞赛不涉及的新版内容外)。打竞赛时养成用且只用 #include <bits/stdc++.h> 的习惯,平时打竞赛也不要用 <ext/pbds> <windows> <dev/random> 等非标准内容。特别注意头文件里的斜杠是正着 / 不是反着,否则 NOILinux 会编译错误!

命名冲突

问:自建变量 y1 next pipe 等和标准库重名爆零怎么办?

答:namespace 包裹自己的程序

#include<bits/stdc++.h>

using namespace std;

namespace my_namespace{
    // 我的程序
    int main(){
        // 我的程序
        return 0;
    }
};

int main(){ return my_namespace::main(); }

这样就会优先读你定义的名称,不会因为库函数重名错误。另外,不要用宏 #define 改名字

少用宏

问:#define 让我爆零怎么办?

答:非必要不用宏,用 using 和函数等代替。

宏是在代码文字层面的操作,会有各种意想不到的错误。

例如: - #define M(x,y) x=(x+y)%mod 然后 M(a,n>>2); 会变成 a=(a+n>>2)%mod;,然后 + 的运算优先级高于位运算,a=((a+n)/4)%mod;,爆零。 - #define lowbit(x) ((x)&-(x)) 然后 lowbit(f(n)) 会导致函数 f(n) 被调用两遍,复杂度和副作用都爆了。 - #define 和标准库冲突的名字,能穿透所有 namespace 直接让你爆零。 - 忘记加 #undef 导致后面都错了。

除了 #ifdef 包裹调试语句之类的必要情况,或你非常熟练的模板,都不要用宏!即使是必要情况,也一定要加对应的 #endif #undef 等,并且尽量别用括号里有参数的宏!

危险程度:反复改代码 > 用宏 > 用其他语法!

常见替代方案:

  • #define int long long:用 using ll = long long; 代替(不要改 intll 之类的简写就行),以及代码编辑器的查找替换功能代替。
  • #define lowbit(x) ((x)&-(x)):用函数 int lowbit(int x){return x&-x;} 代替。现在都有 -O2,这不会变慢!
  • #define endl '\n',用了自己的命名空间后,你可以在 my_namespace 里定义 const char endl='\n';
  • 局部复用代码片段(例如写双指针复用指针移动操作):使用函数、lambda 表达式等代替。(平时不熟悉的不建议用 lambda)

编译器帮你查错

问:函数没返回值/隐式类型转换出错/重名变量等查不出怎么办?

答:添加编译选项 -Wall -Wextra -Wconversion -Wshadow 让编译器帮你检查。

  • -Wall 警告很多东西例如未使用的变量,未初始化的变量,%dlong long,if-else 嵌套不写大括号有歧义等。
  • -Wextra 额外警告一些东西。
  • -Wconversion 警告隐式类型转换的问题,比如 intunsigned int/size_t 比较会导致 -1 > 0u 乃至 vector a; for(int i=1;i<=a.size();i++); 死循环。
  • -Wshadow 警告局部和全局,内层循环和外层变量同名。
  • -fno-ms-extensions 关闭一些和微软编译器一致的特性,比如不标注返回值类型的函数。只有 Windows 需要,Linux 的默认就有。
  • 开多了会有很多警告,编译信息太长翻不完,建议给编译命令最后添加 2> log.txt 重定向编译信息。以免其实没错还在虚空调试。Linux 也可以在编译命令最后再加 && echo ok,这样编译成功包括无错误只有警告时也会最后输出 ok

除此之外还有 -Wformat=2 更严格检查格式化字符串(但建议直接用 cin/cout),-Wpedantic 检查非标准代码(比如 (Node){u,v,w}),等其它查错。这些 OI 没用,所以不推荐。

调试工具

问:有什么工具帮我查运行错误/下标越界/数值溢出?

答:按需求添加编译选项 -g3 -fsanitize=address,undefined -D_GLIBCXX_DEBUG 等并使用对应的 gdb asan 等工具。

注意这些会让程序变慢,测速不要加,而且不能和 -O2 一起加。

  • -g3对应 gdb 不赘述了,基础调试工具。不过,有时候直接输出调试更好;标准错误输出 stderr(比如 cerr<<x<<endl;)可以立即输出,多输出几个断点也能找到 RE 在哪。
  • -fsanitize=address,undefined 添加后可以检查一些数组越界和无符号溢出(取模题很有用)等,参见:Awesome sanitizers
  • -D_GLIBCXX_DEBUG 可以让一些库函数和 STL 多检测(例如开了之后 lower_bound 会先检查是不是排好序了)。这个检测可能会提高程序的时间复杂度,测大数据不要开。

忘改调试/注释

问:调试的时候注释了 freopen 忘记恢复/忘删调试语句爆零?

答:采用宏 #ifdef MYDEBUG,不要注释。

采用 #ifdef MYDEBUG #endif 包裹调试语句,采用 #ifndef MYDEBUG #endif 包裹调试要屏蔽的语句(比如 freopen)。调试时,给编译选项加上 -DMYDEBUG 即可。

另外:Windows/Linux 命令行都可以用 2> 把错误输出定向到文件。例如 Linux 下 ./aa < aa.in > aa.out 2> log.txt 可以从 aa.in 读数据,向 aa.out 写数据,把 cerr 都输出到 log.txt。特别地,2> NUL 忽略错误输出,> NUL 忽略输出。(程序本身没 freopen 对应的 stdin stdout stderr 才有用)Windows 也可以,除了 ./aa 改成 aa.exe

输入输出优化

问:用什么输入输出?要读入优化吗?

答:平时用关同步 cin/cout,量巨大时用 cin.read() cin.gcount()/cout.write() cout.flush()

注意:

  • 关同步。在 freopen 后面加上 ios::sync_with_stdio(false); cin.tie(nullptr);
  • '\n' 代替 endl 以免刷新输出缓冲区开销。
  • 需要记一下设置输出小数格式的语句 cout << fixed << setprecision(10) << endl; 从此开始保留固定 $10$ 位小数;cout << defaultfloat << setw(9) << a << endl; 从此开始保留 $9$ 位有效数字。(这里省略了 std::
  • 奇奇怪怪的输出格式要求建议手写函数转成字符串输出。
  • 量巨大:一般 cin/cout 调用总次数(比如输入输出数字个数)超过 $2\times 10^6$ 次算巨大。输入输出单个长字符串开销不大。

建议不用 scanf/printf,现在效率一般不如关同步 cin/cout,且格式化字符串和取地址容易出错。调试也用 cerr 即可(嫌格式麻烦可以把调试输出语句单独抽出来写成函数)。

对于很大的输入输出,手写函数解析输入输出字符串,然后使用 cin.read() cin.gcount() 一次性输入大量内容,使用 cout.write() cout.flush() 一次性输出大量内容。(和 fread/fwrite 功能相同,但更可读)一般建议开一个 $2^{16}$ 字节的 char 数组做输入输出缓冲区(也就是一次性最多读写这么多内容),开小了会慢,开太大太占内存而且卡缓存。

注意手写之后就不能和普通 cin/cout 混用了。cerr 因为是独立的错误输出所以不受影响。

示例代码:

// 感谢UT和前人提供的基础代码。示例代码为了简短只有 int
#include <bits/stdc++.h>
using namespace std;
const int S = 1 << 16;
char buf[S], *p1, *p2, obuf[S], *O = obuf;
int getChar() {
  if (p1 == p2) {
    p2 = (p1 = buf) + cin.read(buf, S).gcount();
    if (p1 == p2) return EOF;
  }
  return *p1++;
}
int readInt() {
  bool f = false;
  char ch;
  while (!isdigit(ch = getChar())) f |= ch == '-';
  int x = ch & 0xf;
  while (isdigit(ch = getChar())) x = x * 10 + ch - '0';
  return f ? -x : x;
}
void putChar(char c) {
  if (O == obuf + S) cout.write(O = obuf, S);
  *O++ = c;
}
void printLine(int x, char c = '\n') {
  if (x < 0) putChar('-'), x = -x;
  if (!x)
    putChar('0');
  else {
    static char stk[21];
    int t = 0;
    while (x) stk[t++] = x % 10 | '0', x /= 10;
    while (t) putChar(stk[--t]);
  }
  putChar(c);
}
int main() {
  cin.tie(nullptr)->sync_with_stdio(false);  // cin 另一种关同步写法
  printLine(readInt());
  return cout.write(obuf, O - obuf).flush(), 0;
}

开栈空间

问:我本地测试怎么开大栈空间?

答:Linux(含 NOILinux)在终端运行程序前先运行指令 ulimit -s 1000000,Windows 编译选项添加 -Wl,--stack=1000000000

Linux 下 ulimit 可以调整终端包括它调用的程序的诸多限制,-s 1000000 表示把栈空间设为 $10^6KiB$(略低于 $1GiB$)。Windows 栈空间可以在编译时添加 -Wl,--stack=1000000000 设置为上限 $10^9$ 字节(略低于 $1GiB$)。当然栈空间就算取消限制还是算在总内存限制的。

不要设置为 unlimited,防止由于无限递归导致系统崩溃。

默认情况下,Windows 栈空间通常是 $2MiB$,Linux 通常是 $8MiB$。这样如果 dfs 太多层会超过栈空间上限运行时错误崩溃。好消息是 NOI 系列正式比赛都会开无限制栈空间,不超总内存限制就行。

测时间和内存

问:如何测自己程序时间和内存?

答:Linux 用 time ./程序名 测时间,ulimit -v <内存限制KB数> 测超内存限制。Windows 用程序内 clock() 测时间,用命令行 cmdsize 测内存。

Linux 编译好程序 aa 后用 time ./aa 测时间(要定向输入输出就 time ./aa < aa.in > aa.out),看的是几个值中的用户时间(user);用 ulimit -v 524288 限制内存到 $512MiB$,建议实际开 ulimit -v 500000 即可,留点空余。这个测试方法和 NOI 规则相同(看的是申请空间而不是实际使用的页数)。

想改变 ulimit -v 内存限制请关掉终端重开。 同一个终端 ulimit 开内存只能从大往小开,不能反着来。

Windows 在程序里添加 cerr << clock() << endl; 来输出运行时间(建议也和其他调试一样用 #ifdef MYDEBUG #endif 包裹)。用命令行 cmdsize aa.exe 命令测内存。

此处没有推荐难记的做法比如 std::chrono,会的可以用。

注意手动输入数据也算时间,测时间请开 freopen 或者用 < aa.in > aa.out 定向输入输出文件。

考试环境是 Windows

问:考试环境是 Windows 怎么办?还有前面没提到的吗?

答:务必打开显示文件扩展名。

你可以本文内搜索 Windows 这个词。

  • 如果你使用 Windows 系统,请务必把文件查看中的“显示文件扩展名”打开,不然你命名为 a.cpp 实际上是 a.cpp.cpp 直接爆零。

不要写超标语法

问:用超标/被禁语法爆零了怎么办?

答:平时就别写超过 -std=c++14#pragma 之类的违禁品。

至于有啥?自己开编译器 -std=c++14 测!NOILinux 默认这个不用开。

样例

  • 小明参加 NOIP。
  • 小明详细读完了四个题并手算了每个题最小样例,期间看完第一题直接顺利想出了做法,这些一共花了 15 分钟。题目的输入输出文件名是 aa problemb cti di。(现在是 15 分钟)
  • 小明想了想第二题,10 分钟想出了 60 分做法,满分暂时不会。他又去想第三题,10 分钟想出了第三题做法是 DP,但是不好写,好写的暴力是 30 分其它部分分也不好写,于是又花 10 分钟想清楚写下几个主要函数的外壳和关键的状态数组在原问题的准确意义。剩下第四题看了 5 分钟啥也不会,只能写 20 分暴力,如果用数据结构维护可以到 45 分,但问题本身就复杂要写很久。(现在是 50 分钟,小明会上限 305 分,其中 210 分好写)
  • 小明 20 分钟写完第一题并通过所有大小样例,手造的 1 0 边界数据通过,极限数据开查错编译/调试选项测试报了个越界,发现一个数组下标开错了,花 10 分钟。(现在是 1 小时 20 分钟,写了 100 分)
  • 小明 30 分钟写完第二题 60 分并通过小样例,特殊性质大样例没过,查错编译/调试选项报了个 int 溢出,发现是有个地方取模漏了。10 分钟解决通过特殊大样例。(现在是 2 小时,写了 160 分)
  • 小明再想想第二题满分怎么做,但几分钟无果,上了个卫生间放松还是没思路,决定继续写后面的。(现在是 2 小时 10 分钟)
  • 小明觉得第三题难写,决定先写第四题暴力。15 分钟写了 20 分通过小样例。(现在是 2 小时 25 分钟,写了 180 分)
  • 小明利用代码框架在 45 分钟内写出了第三题,但过不了小样例。开查错编译/调试选项测试没错。先测了手造的 1 1 边界数据过不了,花了 5 分钟修好,但还是过不了小样例。考虑到其他题都写过了,小明决定开始对拍调试。(现在是 3 小时 15 分)
  • 小明 10 分钟写完了小暴力和数据生成器并测试暴力过了小样例。(现在是 3 小时 25 分)
  • 小明开始从小到大对拍数据,在 n=4 拍出错了,他把权值手动改小找到了更简单的错误数据。然后小明在代码中间用 #ifdef MYDEBUG cerr #endif 加了调试输出中间结果,并用 #ifndef MYDEBUG #endif 框住了 freopen,重新开查错编译/调试选项编译时添加了 -DMYDEBUG,方便调试时读其他文件 ./cti < cti.in > test.out 2> cerr.log。他运行小数据并手算中间状态的数学意义该有的值,找到了几个错误。反复几次后他暴力能跑的范围小数据对拍上了,并且极限数据也没报错。这些调试用了 30 分钟。(现在是 3 小时 55 分,写了 280 分)
  • 现在小明时间不多了,决定再去卫生间冷静一下。突然他想到利用反悔贪心的思路能解决第二题,并且应该好写。他尝试脑内调整法大概验证了下正确性证明思路。(现在是 4 小时)
  • 小明把原来的第二题代码备份了一下,然后开始写第二题满分做法。但这时候电脑卡死了,小明举手示意监考,监考无法解决只能重启,给小明加时 5 分钟。(现在是 4 小时 5 分)
  • 幸运的是,因为贪心代码很简单,他 20 分钟就写完并且过了大小样例,开查错编译/调试选项也没报错。(现在是 4 小时 25 分,写了 320 分)
  • 小明最后 5 分钟检查程序,先关闭代码文件,检查了文件夹路径和文件名,然后复制题面PDF第一页的编译选项,没加自己的选项编译通过了四个题,并且运行了小样例,肉眼和 diff 分别验证了小样例正确性。然后关闭了所有命令行和窗口,考试结束了。
  • 加时的 5 分钟小明又检查了一次文件夹,文件名和编译通过。
  • 成绩出来了,小明第二题和第三题还是挂了 3 个特殊边界数据点,最后总分 $305$。
  • 小明感叹一声:还好不是 CSP-S,不然只有 4 个小时时间,自己恐怕没时间做第二题了。

防爆零指南之:其他内容推荐和琐碎细节合集

看太多会记不过来,而且其中重要内容在前面都有了。没时间不用看。

如果你想看:

部分我查过的其它资料列表(不要通读它们,本文足够)

我的个人链接(厚颜无耻)

从理解斜二倍增,到放弃斜二倍增

2025-10-30 04:26:21 By liu_cheng_ao

对于梦熊联盟,以及其实际控制人和主要管理人员实际控制的其它机构,禁止任何人于正在临时或长期参与其任何活动期间,以任何直接或间接方式转载,引用,使用,传播或展示:本人在2024年5月或以后创作的任何内容或片段,包括所有声明为公开发表的内容在内!学生或教师本人自行阅读学习为被允许的例外情况,但此授权不包括在上述实体活动期间在其平台上进行转载,引用,传播等。

公开资源推荐和征集:

什么是斜二倍增?我看不懂斜二倍增怎么办?

问题: 我有一棵有根树,想支持在线动态 $O(1)$ 加叶子,在线 $O(\log n)$ 查询路径可合并信息(比如路径最大值个数,路径最大子段和),内存 $O(n)$,怎么办?

在下文中,为了简便,我们设定:查询的信息基于点权而非边权。

传统解法:

  1. 直接上LCT:但是难写且常数大。
  2. 树剖:因为动态加点得随机选重儿子,但是如果数据不随机又得上平衡调整,然后你发现自己发明了LCT。
  3. 每 $\log n$ 层树分块/取关键点:这思路能做,但还是没那么好写。
  4. 树上倍增:好写好用,但是每个点要 $O(\log n)$ 预处理,怎么办?

我们考虑怎么优化树上倍增。树上倍增预处理的信息总共 $O(n\log n)$ 个,这很有用,但是做路径查询其实不需要这么多。你看线段树,树剖+线段树都是 $O(n)$ 空间就能做路径查询了。能不能对倍增也这么干?

尝试直接让线段树上树。方便起见,我们取完美线段树(区间长度都恰好是 $2$ 的幂):我们对于树上每个到根路径,把它当成一个深度为下标的序列,预处理出线段树上所有应该有的点。

也就是说,对于每个树上的点 $u$,设它的深度为 $d(u)$,那么:线段树上每有一个以 $d(u)$ 为右端点的长度 $L$ 的区间,我们就预处理从 $u$ 向上 $L$ 个点的路径段信息。(深度从 $1$ 开始算)

线段树.png

可以发现,这等于:对于每个点 $u$,我们处理了向上长度 $2^1,2^2,\dots ,2^{\mathrm{lowbit}(d(u))}$ 的所有区间。也就是我们对树上倍增添加一个上限 $2^{\mathrm{lowbit}(d(u))}$。我们可以像线段树那样,查询每个点到根路径的任意区间,那么仍然可以 $O(\log n)$ 查找LCA以及求路径可合并信息。

这样,我们已经把树上倍增几乎变成线段树的 $O(n)$ 复杂度。

但还有一个小问题:线段树虽然总复杂度是 $O(n)$ 的,但是单个位置为右端点的区间数仍然可以达到 $O(\log n)$。那么树上这种深度的节点很多时复杂度还是会退化到 $O(n\log n)$。如果能把线段树每个区间右端点变成互不相等的,就能解决这个问题。

斜二倍增

这其实很简单:我们把每个线段树区间的定义从两个子树区间的并,改成:两个子树区间的并后面再多加一个点。也就是形如:

斜二线段树.png

这个线段树称为斜二进制线段树。这样每个点存的区间数都变成了严格的 $1$ 个,而且仍然可以线段树查询(只是每一层要多合并一个单点)。具体来说:设 $L(x)$ 表示以 $x$ 为右端点的斜二进制线段树区间长度(上图橙色数字),则对于每个树上的点 $u$,设它的深度为 $d(u)$,我们预处理从 $u$ 开始向上长度为 $L(d(u))$ 的路径段信息即可。$L$ 数组倍增预处理即可。

之后的用法就和普通倍增一样,具体代码可以参考介绍博客 - UT

我不想学斜二倍增!普通倍增能代替吗?

其实可以,直接按前文所述对倍增添加一个上限 $2^{\mathrm{lowbit}(d)}$即可。此处 $d$ 是倍增起点的深度或下标。

那么怎么解决同深度点太多时复杂度不对的问题呢?

方法一. 如果问题可以换根,可以先遍历树找一个好的根,总存在一个根使得每个点深度的 $\mathrm{lowbit}$ 位数和是 $O(n)$ 的。

证明和构造思路:考虑对树进行黑白染色,取黑/白根时所有同色(或者异色,看深度定义)点的深度全为奇数。所以递归到较小颜色为偶数的情况里选取,然后继续递归即可。

方法二. 这个更简单,而且通用:我们一开始均匀随机选取一个全局的 $[1,2^{\lceil \log n \rceil}]$ 正整数常数 $C$,然后我们定义 $\mathrm{lowbit'}(x)=\mathrm{lowbit}(x+C)$,改用 $\mathrm{lowbit'}$ 做倍增上限即可。

证明思路:这相当于把所有下标和线段树区间整体平移了一下,容易发现不影响线段树的复杂度。但这会让每个点 $\pmod{2^{\lceil \log n \rceil}}$ 都变成均匀随机的,而均匀随机整数的 $\mathrm{lowbit}$ 期望(平均)等于 $1$。所以期望复杂度正确。

缺点:倍增数组不定长,内存分配可能是个问题。不过出题人不刻意卡的话 vector 即可。卡就手动分一下。

因为本来就差不多(随机化和确定性的区别)目前可以完全代替斜二倍增,适合不想学新代码的选手。

题外话:对于有些问题倍增 $O(n\log n)$ 个信息是必要的。比如树上求两个路径的最长公共前缀二分哈希时,必须要任意两个点都能走相同长度。

我普通倍增也不想学了

树分块:每 $\log n$ 层树分块/取关键点,关键点个数是 $O(n/\log n)$ 然后关键点之间跑带 $\log$ 的算法,查询的时候暴力跑到第一个关键点即可。

代码

随机偏移倍增(方法二)求 LCA 的核心代码(我写的比较繁杂,可以根据你自己的倍增代码修改):

和普通倍增唯一区别是倍增步数取了个 $\min$。

int L(int x){return __builtin_ctz(x+C);} // lowbit位数。其中C是全局预处理常数,在O(n)值域随机的一个正整数
int B(int x){return 31-__builtin_clz(x);} // 二进制最高位数
// dep:深度 f[i][j]:i的2^j级祖先,其中j<=min(B(dep[i]),L(dep[i]))
int lca(int u, int v){
    if(dep[u]<dep[v])swap(u,v);
    int dd=dep[u]-dep[v];
    while(dd){ // 跳到dep[u]==dep[v]
        int di=min(L(dep[u]),B(dd));
        u=fa[u][di], dd-=(1<<di);
    }
    for(int i=B(dep[u]);i>=0;){
        int di=min(min(B(dep[u]),L(dep[u])),i);
        if(fa[u][di]==fa[v][di])
            i=min(i,di-1); // 不用再尝试>=di的了
        else
            u=fa[u][di], v=fa[v][di];
    }
    if(u!=v)u=fa[u][0],v=fa[v][0];
    return u;
}

坚决反对与虎谋皮,以营销炒作的方式“推广知识”,破坏社区秩序的行为!

坚决反对通过向部分不懂算法的家长/教练危言耸听,吹嘘特定知识重要性,强迫学生学习,破坏教学秩序的行为!

坚决支持建设高质量公开资料推荐平台和日报刊物平台!

对于梦熊联盟,以及其实际控制人和主要管理人员实际控制的其它机构,禁止任何人于正在临时或长期参与其任何活动期间,以任何直接或间接方式转载,引用,使用,传播或展示:本人在2024年5月或以后创作的任何内容或片段,包括所有声明为公开发表的内容在内!学生或教师本人自行阅读学习为被允许的例外情况,但此授权不包括在上述实体活动期间在其平台上进行转载,引用,传播等。

liu_cheng_ao Avatar