2026春好题记录
P3971 [TJOI2014] Alice and Bob 5.9
我大抵是睡着了。
tag:Ad-hoc,贪心,图论建模
题意:有一个未知序列 \(\{x_1,x_2,\dots,x_n\}\),已知以每个 \(i\) 为结尾的最长上升子序列的长度,求以 \(i\) 开头的最长下降子序列的长度之和的最大值。
先考察原数列有没有值相等的情况,发现你可以将前一个调大,后一个调小,这样肯定不劣。于是原数列一定得是一个排列。
再考察数列的最长上升子序列,发现若存在 \(i<j\),\(a_i=a_j\),那么 \(x_i>x_j\)。而对于 \(x_i\),以它为结尾的最长上升子序列长度 \(a_i\) 一定是从 \([1,i)\) 中的一个 \(a_i-1\) 转移过来的。
那么根据上述性质,\(x_i\) 一定比最后一个 \(x_j=a_i-1\) 的 \(x_j\) 要大。而且如果 \(x_i\) 更大的话 \(b\) 会更小,所以你考虑图论建模,对于每个 \(i\) 找到 \(j\),\(j\rightarrow i\) 连一条边,表示偏序关系。然后对于同一层的点,靠后的需要更小,于是你按输入顺序的倒序连边,跑 \(\text{dfs}\),每个点的 \(\text{dfn}\) 即为 \(x_i\)。
再跑一边最长下降子序列即可。
[ARC107D] Number of Multisets 5.2
虽然不难,但很有意思的题。
加深此类 trick 的印象。
tag:dp
题意:求可重集 \(S\) 的个数,满足 \(|S|=n\),\(\sum\limits_{k\in S}k=m\),\(\forall k\in S,\exists t\in \mathbb{N},k=2^{-t}\)。
相当于你可以对 \(n\) 个 \(1\) 做若干次前缀 \(\times \frac{1}{2}\) 操作使 \(\sum\limits_{k\in S}k=m\)。
设 \(dp_{i,j}\) 表示让前 \(i\) 个数之和为 \(j\) 的方案数,转移有:
- 所有数 \(\times \frac{1}{2}\),则 \(dp_{i,j}\leftarrow dp_{i,j\times 2}\)。
- 加入一个 \(1\),则 \(dp_{i,j}\leftarrow dp_{i-1,j-1}\)。
这样就在 \(O(n^2)\) 内解决了这个问题。
P8688 [蓝桥杯 2019 省 A] 组合数问题 题解 6.0
tag:Lucas 定理、数位 dp
题意:给定 \(n,m,p\),求 \((i,j)\) 的对数满足 \(1 \le i \le n,0 \le j \le \min(i,m)\) 且 \({i\choose j} \equiv 0\pmod{p}\),\(p\) 是质数。
观察到这个条件是一个组合数对一个质数取模后的东西,可以联想到 Lucas 定理。则
其中 \(i_k,j_k\) 分别是 \(i,j\) 在 \(p\) 进制意义下的第 \(k\) 位。那么要想让这么多个数乘起来是 \(p\) 的倍数,则其中必有一个数是 \(p\) 的倍数。即存在 \(k,{i_k\choose j_k}\equiv 0\pmod{p}\),等价于 \(i_k<j_k\)。
存在这件事难以刻画,我们考虑容斥,现在需要求 \((i,j)\) 的对数,其中 \(i,j\) 在 \(p\) 进制下的每一位 \(i_k,j_k\) 都满足 \({i_k\choose j_k}\not\equiv 0\pmod{p}\) 即 \(i_k\ge j_k\)。
那么就可以开始数位 dp 了,设 \(f_{i,0/1,0/1}\) 表示从高到低考虑到前 \(i\) 位,\(i\) 有没有顶到头,\(j\) 有没有顶到头。转移进行分讨即可。
时间复杂度 \(O(T\log_{k}n)\)。
[AGC054C] Roughly Sorted 5.2
tag:计数
题意:有一个排列 \(P\),可以交换 \(P\) 的相邻两项,使其每个 \(i\) 的逆序对数不超过 \(k\)。你得到了经过最小次数操作后的排列 \(P'\),求有多少种可能的原始排列 \(P\)。
该加训此类题型了。
考虑刻画逆序对个数这件事,你发现如果 \(P\) 的逆序对个数不超过 \(k\),那么 \(1\) 的位置肯定不能超过 \(k+1\)。同理 \(i\) 的位置肯定也不能超过 \(k+i\)。那么你就有 \(P'_i\le k+i\)。
如果 \(P'_i<k+i\),因为你要最小化操作次数,所以在原排列中这个 \(i\) 的位置本来就会在这里,要不然你也不会去把他往左边移动。
所以我们只需要考虑 \(P'_i=k+i\) 的贡献,它一共会有 \(n-k-i-1\) 种取值。
[ARC139D] Priority Queue 2 6.2
tag:求和转 01、计数
题意:给你一个数列 \(a_i\in [1,m]\),你要进行 \(k\) 次操作,每次操作形如:选择一个 \([1,m]\) 中的整数,加入 \(a\),然后删去 \(a\) 中第 \(x\) 小的元素。求所有方案操作后 \(a\) 的和之和。
考虑把求和形式改为 01 序列形式,有 \(\sum\limits_i a_i=\sum\limits_j\sum\limits_i[a_i\ge j]\)。于是考虑枚举 \(j\),计算 \(\sum\limits_i[a_i\ge j]\) 的总和。
设初始时满足 \(a_i\ge j\) 的 \(i\) 的个数为 \(c\),现在一次操作即为:
- \(c\) 每次有 \(m-j+1\) 种可能 \(+1\),若 \(c\ge n-x+1\) 则 \(c\gets c-1\)。
枚举 \(c\) 被增加的次数 \(l\),则这种变化的可能情况有 \(\binom{k}{l}(m-j+1)^l(j-1)^{k-l}\) 种,而最终 \(a_i\ge j\) 的个数也同样可以计算:
- 若 \(c\ge n-x+1\),则最终 \(a_i\ge j\) 的个数为 \(\max(n-x+1,c+l-k)\)。
- 若 \(c<n-x+1\),则最终 \(a_i\ge j\) 的个数为 \(\min(n-x+1,c+l)\)。
直接统计即可。时间复杂度 \(O(mk)\)。
#8692. Yet Another Convolution 6.0
tag:(整体)二分、莫反
题意:给你两个数列 \(a,b\),求 \(c_k=\max\limits_{(i,j)=k}|a_i-b_j|\)。
如果可以快速计算 \(c_1\),计算它的时间复杂度是 \(T(n)\),那么 \(c_i\) 都可以在 \(T(\frac{n}{i})\) 的时间复杂度内解决。
不妨设 \(a_i\le b_j\),我们现在需要找到最小的 \(b_j\) 满足 \(i\perp j\)。考虑整体二分,转化为存在性问题,只需要判断是否存在 \(b_j\le x\) 且 \(i\perp j\)。有 \(\sum\limits_{j=1}^{n}[b_j\le x][i\perp j]=\sum\limits_{d\mid i}\mu(d)\sum_{d\mid j}[b_j\le x]\),第二个 \(\sum\) 容易优化,时间复杂度可以做到 \(O(\sum d(i))=O(n\log n)\),那么 \(T(n)=O(n\log^2n)\),总时间复杂度为 \(O(n\log^3n)\)。
/se 6.0
tag:分析性质、dp
题意:将 \(n\) 条线段划分为不超过 \(k\) 个集合,求每个集合中所有线段的交的长度之和的最大值。
对于两个线段 \(j\subseteq i\),如果 \(i\) 与 \(j\) 被分到一组,那么 \(j\) 不会造成任何影响。为了让总长度最大,我们要么把被包含线段 \(j\) 单独放进一个集合,要么就把它随便扔进一个包含它的集合里。
当我们把这些线段删去之后,剩下的线段有一个很好的性质:如果将这些线段按左端点从小到大排序,那么它们的右端点也一定是严格单调递增的。
考虑 dp,设 \(dp_{i,j}\) 表示前 \(i\) 条前段划分成了 \(j\) 个集合的答案。最后 \(dp_{n,j}\) 和被删去的线段合并即可。时间复杂度 \(O(nk)\)。
叁仟肆佰万 5.5
tag:计数、dp
题意:求分割一个序列使得每段 \(\operatorname{mex}\) 值相等的方案数。
你发现如果一段区间有 \(0\),那么其他区间也必须有 \(0\)。以此类推,可以得到每段区间的 \(\operatorname{mex}\) 就是序列的 \(\operatorname{mex}\)。
然后对于一个右端点 \(r\),合法的 \(l\) 应该是一段前缀,所以你双指针维护即可。时间复杂度 \(O(n)\)。
P4099 [HEOI2013] SAO 6.5
tag:计数、dp
题意:给你一棵有向树,求其拓扑序个数。
设 \(f_{u,i}\) 表示以 \(u\) 为根的子树内 \(u\) 的拓扑序在第 \(i\) 个的拓扑序的个数。
考虑对于 \(u\) 枚举它的儿子 \(v\),对这条边的方向进行讨论:
- \(u\rightarrow v\)。考虑如何从 \(f_{u,k}\) 转移至 \(f_{u,i}\)。最终的拓扑序形如 \(\dots u\dots v\dots\),那么你就需要将 \(u\) 的拓扑序列和 \(v\) 的拓扑序列拼起来,其中需要从 \(v\) 的前几个数里边拿几个塞到 \(u\) 前面,使得它从第 \(k\) 位变成第 \(i\) 位,容易得到转移
- \(v\rightarrow u\),最终的拓扑序形如 \(\dots v\dots u\dots\),后续步骤和上述情况相似。
然后可以使用前缀和优化。时间复杂度 \(O(n^2)\)。
CF1146H Satanic Panic 6.2
tag:计算几何、dp
题意:求平面 \(5\) 个点的凸包数量。
考虑凸包有一个性质,它上面的边斜率按一定顺序单调递增,那你就可以开始 dp 了。
先把 \(n^2\) 条边搞出来排个序。考虑 dp,设 \(f_{i,j,k}\) 表示从 \(i\) 走了 \(k\) 步到达 \(j\) 的方案数。
然后按边的顺序更新,对于 \((u,v)\),转移有 \(f_{i,v,j}\leftarrow f_{i,v,j}+f_{i,u,j-1}\)。
时间复杂度 \(O(n^3k)\),\(k\) 在此处为 \(5\)。
P14637 [NOIP2025] 树的价值 7.5
tag:发现性质、dp
题意:给你一棵树,给每个点设点权,使 \(\sum \operatorname{mex}(S_u)\) 最大,其中 \(S_u\) 表示 \(u\) 的子树点权集合。
好题啊。
你先考虑整棵树的结构:对于每个点,它肯定是先从某个儿子的 \(\operatorname{mex}\) 转移过来,然后使用若干还没被使用的点(称为自由点)得到的。
那么现在就有一个 \(O(n^3)\) 的 dp 了:\(dp_{u,i,j}\) 表示 \(u\) 子树 \(\operatorname{mex}\) 为 \(i\),还有 \(j\) 个自由点,得到的子树最大价值和。有 \(48\) 分。
这个状态不能接受。但是这道题给出了深度的性质:\(m\le 800\)。这启发我们考虑设深度有关的状态。
想到链剖分。考虑将转移过来的那个点记为重点,其余点记为轻点,那么现在这棵树就变成了一个剖分结构。

考虑拆贡献,容易发现对于一个点,当它对答案产生贡献时,它肯定从一个自由点变成了不自由点。这个贡献会从它产生贡献开始,一直往上爬重链,直到它被弃用,即到达这个重链的顶部。
因此每个点产生的最大贡献为其上重链链长的最大值。
那么现在就有一个 \(O(nm^2)\) 的 dp 了:\(dp_{u,i,j}\) 表示 \(u\) 到根节点的重链最长为 \(i\),它现在所在链长为 \(j\),得到的子树最大价值和。转移是简单的。有 \(76\) 分。
这个状态还是不能接受。观察上面所述的状态,这个 \(j\) 的定义其实非常浪费。
分析性质,对于一个点 \(u\),实际上只有如下几种情况:
- \(u\) 子树内所有重链都不如 \(u\) 祖先的最长重链长,可以将 \(u\) 子树内所有重链删掉,增加价值;
- \(u\) 所在的重链是最长的 \(u\) 最先的最长重链,那么你不需要改动;
- \(u\) 所在的重链较短,而子树内有一条重链较长,可以将所有较短的重链删掉,将那条重链一直传递到 \(u\),从而变为以上两种情况。
那么可以设计出这样的状态:\(f_{u,i}\) 表示点 \(u\) 的贡献为 \(i\),并且 \(k=1\);\(g_{u,i}\) 表示点 \(u\) 的贡献为 \(i\),并且 \(k=i\)。
考虑转移。\(g\) 的转移是简单的:
可以 \(O(nm)\) 做。
考虑 \(f\) 的转移。首先若 \(u\) 子树内全是轻点,那么 \(f_{u,i}\leftarrow i\times sz_u\)。
否则 \(u\) 的子树内存在一个重点 \(v\)。那么这个新的链就需要产生作用,也就是说对于 \(f_{u,i}\),链长需要不少于 \(i\)。而且对于 \(u\) 到 \(v\) 链上的点 \(w\),它们的儿子也会是轻点,否则将他们改成轻点对答案的贡献一定会更多。那么设 \(u\) 和 \(v\) 距离为 \(i\),有转移
用你喜欢的数据结构进行维护即可。遍历子树内每个点的时间复杂度是 \(O(\sum sz_u)=O(nm)\) 的,这部分的时间复杂度即为 \(O(nm\log n)\)。
P14638 [NOIP2025] 序列询问 7.0
tag:数据结构
题意:给你 \(q\) 个询问,每个询问有一个区间 \([L_i,R_i]\),每次询问,对每个 \(i\in[1,n]\),你需要求出 \(\max\limits_{1\le l\le i\le r\le n}\{\sum\limits_{i=l}^{r}a_i \mid L_j\le r-l+1\le R_j\}\)。
考虑直接拍一个裸的滑动窗口,这就有 \(45\) 分了。那这题应该跟滑动窗口有很大关系。
考虑性质 D,区间一定过中点。那么你考虑将区间分成两半,区间和的 max 就相当于左半边区间和的 max 加上右半边区间和的 max。求出前后缀的 max,当你固定右端点时,合法的左端点就是一段区间,直接做滑动窗口即可。
考虑性质 E,将序列分成四块即可。那你应该就能联想到正解了:考虑倍增分块,分出 \(O(\log n)\) 个块后进行统计即可。
P6891 [JOISC 2020] ビルの飾り付け 4 5.5
tag:dp、交换两维
题意:给定两个长度为 \(2n\) 的序列 \(A,B\),构造一个长度为 \(2n\) 的序列 \(C\) 满足:\(C_i\in \{A_i,B_i\}\);\(|\{i\mid C_i=A_i\}|=|\{i\mid C_i=B_i\}|\);\(C_i\ge C_{i-1}\)。
先考虑一个很显然的 dp:设 \(f_{i,j,0/1}\) 表示前 \(i\) 位填了 \(j\) 个 A,第 \(i\) 位填了 A/B,可不可行。时间复杂度 \(O(n^2)\),不能通过。
考虑猜测如果 \(i,0/1\) 固定,那么合法的 \(j\) 形成了一段区间。这是因为 \(A_i\),\(B_i\) 中较大数能转移的状态一定包含了较小的数能转移到的状态。那么直接 \(O(n)\) 维护即可。
P9870 [NOIP2023] 双序列拓展 6.5
tag:ad-hoc
你考虑这个拓展实际上是在搞什么事。
将其放到一个棋盘上,这相当于:设 \(A_{i,j}=[x_i<y_j]\),你现在从 \((1,1)\) 开始,每次可以向右、下、右下的 \(A_{i,j}=1\) 走,问能否走到 \((n,m)\)。
先考虑特殊性质。首先如果存在 \(x_n>y_i\),那么 \(A\) 的其中一行就都是 \(0\) 了,无解;同理若存在 \(y_n<x_i\) 也无解。那么现在棋盘的第 \(n\) 行和第 \(m\) 列就都会为 \(1\) 了。
着其实给了我们相当大的启发。再考虑找出前 \(n-1\) 行的最小值 \(x_k\):
- 若对于任意 \(i\),\(x_k<y_i\),那么第 \(k\) 行的 \(A\) 就会均为 \(1\),可以缩减问题规模;
- 若存在 \(i\),\(x_k>y_i\),那么第 \(i\) 列的 \(A\) 就会全为 \(0\)。
还可以对前 \(m-1\) 行的最大值进行相似的讨论。那么你发现你不能缩减问题规模的时候,存在一行的 \(A\) 全为 \(0\),存在一列的 \(A\) 也全为 \(0\)。那么此时你就不能到达 \((n,m)\) 了。
那么直接递归处理即可,时间复杂度 \(O(q(n+m))\)。
再考虑正解,这其实没什么区别了。找出 \(x\) 的最小值 \(x_i\),找出 \(y\) 的最大值 \(y_j\)。那么第 \(i\) 行、第 \(j\) 列都为 \(1\)。把棋盘按照这两条线劈四半,就转化为了特殊性质的形式。

浙公网安备 33010602011771号