数据结构好题
[省选联考 2025] 追忆
难度挺恶评的。有向图可达性只会也只能 \(O(\frac{nm}{w})\)。然后你发现修改操作非常强,考虑分块值域。对答案二分,然后 bitset。然而注意到需要对修改时的映射进行神秘操作,这大概是这道题唯一的难度所在。
「蓬莱人形」
Yuno loves sqrt technology II
区间逆序对离线。用二次离线莫队可以把原本 \(O(n \sqrt{n} \log n )\) 的东西变成 \(O(n \sqrt{n} + n \log n)\) 的。一个稍加点拨就可以想到的做法是将所有的贡献存下来,设 \(f(l,r,x)\) 表示对于 \(x\) 这个值在 \([l,r]\) 中和它有几个逆序对,然后差分一下 \(f(l,r,x)=f(1,r,x)-f(1,l-1,x)\),这个就变成了前缀问题,是可以 \(O(n \log n)\) 预处理的,但是空间现在变成了 \(n \sqrt{n}\) 级别,粗略算一下有一个 \(4\) 倍的空间常数,不太能接受啊。
哦哦优化空间只用发现莫队的转移是连续的,把这个东西用一个范围存起来就行了,这样虽然也有常数但是数量级就变成了 \(O(n)\) 的。于是做完了。
[NOIP2025] 序列询问
- \(O(nqlogn)\)
考虑分治。对于左半部分因为要跨过区间所以以当前点为左端点的答案适用于后面所有的答案,于是可以 st 表每次查询,再记录最大值并更新。右半部分同理。
- \(O(nq+n\log^2n)\)
一种剑走偏锋的倍增分块做法。
计算分治的时间复杂度有两种方式:类似线段树地,\(O(\sum len) = O(n \log n)\)。另一种不太常规的做法是用 下界 乘上 区间个数,在本题即为 \(R \times (2 \times n/L)\) 。那么当 \(R/L\) 为常数时,时间复杂度可以变为线性。
考虑二进制。将所有的 \(L=2^i ,R=2^{i+1}-1\) 存下来,然后把这些块类似 ST 表地记下来。最后首末两个散块的 \(L,R\) 一定不超过两倍,暴力地再算两次就可以了。整体是线性的。需要特判如果本来就不超过两倍的情况。
- \(O(nq)\)
树形结合太牛了。
https://www.luogu.com.cn/article/hipjvu7e
https://www.geogebra.org/calculator/unxrcy9t
QOJ #964. Excluded Min
很难想到可以用这样的方式将时间复杂度进行确保。或许可以应用在一些其他的区间问题中(?
https://www.luogu.me/article/hmghl53t
[AHOI2017/HNOI2017] 影魔
是不是很多 \(L \le l \le r \le R\) 的题都可以先算没有区间限制(即全局)的贡献然后通过前缀和什么的减一下变成区间啊。
这个题观察到以一个点作为左端点,第一个前缀最大的贡献是p1,后面的是p2。然后直接对全局做,设每个点的贡献是 \(a_i\) ,统计前缀和,减去 \(r\) 往后的部分是就完了。
TJ还搞了一个 p 点把序列分割成两部分,不理解,应该直接做也行。这个的瓶颈在 rmq 上面。所以可以线性。
另外一种做法是扫描线,直接考虑点对对区间的贡献,你发现设 \(l,r\) 表示 \(i\) 左右边第一个比它大的,然后p1一定是 \(l,r\) 对中间的产生贡献,这是容易的。p2一定是端点加上 \(i\) 对应的另一边来产生贡献。证明的话因为是一个排列,所以一定是不重不漏的。
[Ynoi2011] 初始化
基础分块题。套路地去枚举阈值。在跨度比较大时,我们需要支持一个时间较小开销的单点修改(因为有 \(n/B\) 次操作)和适当的区间查询。然后跨度比较小时,我们可以通过余数进行分类,然后分成一段一段的,分别统计,这个是固定的 \(O(B)\),所以复杂度是 \(O(qn/B \times \texttt{update} +q \times \texttt{query} + qB)\),显然开销在第一部分比较大,我们考虑用普通分块,直接维护块内权值和和单独的实际权值,这样的话,update 部分是 \(O(1)\) 的,查询是根号的,最后平衡一下,总的也是根号。
个人而言,这个题的主要难点在于普通分块(?,用其他的方法很容易退化到 \(O(n\sqrt{nlogn})\) 之类的神秘复杂度,其实是因为 \(\texttt{update}\) 不是 \(O(1)\) 的原因。
还有一个另外的 trick 是查询 \(O(1)\),这个可以通过维护块内前后缀和来实现。
P10093 [ROIR 2022] 礼物
对应这种 k 大问题,我们有超级钢琴(那个列表然后取出最大的一行,把列数向后移一个),二分判定,以及直接枚举第 k 大,而对于这种情况,一个 trick 是把数组按从大到小的顺序加入链表当中再做计数或者统计答案。
而这道题推到这一步之后就是暴力(或许还有一步 rmq)了。仔细想想这个 trick 也没有什么提升空间了。(是不是还可以用平衡树来维护点?)
P17112 「FAOI-R13」Hi, story
月赛题,跟那个矩阵快速幂预留出 \(kx+b\) 然后让 \(x\) 作为占位符的题有相似之处,你考虑用线段树维护 \(gcd(kx+b,c)\) 然后合并两个这样的东西跟普通gcd类似,然后如果要区间加法的话其实直接给 \(b\) 加上 \(k\) 然后打 \(tag\) 就行,因为你仍然要保证占位符一样。
查找 Search
考虑对于每一个数 \(x\),维护它后面第一个 \(w-x\) 的位置 \(nxt\)。问题转换为求 \(\min{nxt_i} \le r (l \le i \le r)\) 这个东西因为满足 \(i \le nxt_i\) 所以一定等价于 \(\min{nxt_i} \le r (l \le i \le n)\),于是考虑怎么修改,对于一个数 \(x\)。
首先是求它自己变完之后的后继,这个搞 \(5 \times 10^5\) 个 set 随便做。然后是它之前的东西,你发现如果都去改的话爆了,那因为我们刚刚巧妙地转化之后变成一个后缀上的神秘东西,这个显然是可以在 \(nxt_i\) 相同的情况下,只改最后一个的。于是做完了,\(O(n \log n)\)。
学这个有什么用啊。

浙公网安备 33010602011771号