金华一中杂题选讲
金华一中杂题选讲
A [BalticOI 2024] Jobs
考虑一个 dp,设 \(f(x)\) 表示为了在 \(x\) 子树内挣到钱,需要的最少的本金。先考虑怎样求答案,我们从根节点出发开始扩展,每次取出当前 \(f(x)\) 最小的值,显然这样一定更优。然后我们加上 \(x\) 的贡献,并把 \(x\) 的所有儿子扩展出去。直到当前取出的最小 \(f(x)\) 大于当前总钱数,说明无法再赚钱了,此时的总钱数减去初始钱数即为利润。
显然上述过程可以用堆维护,复杂度 \(O(n\log n)\)。考虑怎样求 \(f(x)\)。
首先我们可以仿照上面的方法去求,假设当前的总钱数为 \(now\),如果某一次扩展完 \(now>f(x)\) 说明我们赚到钱了,可以退出;否则我们就要继续扩展赚钱,与上面不一样的是,如果此时取出的最小 \(f(i)>now\),我们需要抬高本金,即将 \(f(x)\) 加上 \(f(i)-now\),使得 \(now\) 变为 \(f(i)\) 后再去赚钱。
不难发现这样做的复杂度最坏是 \(O(n^2\log n)\) 的,考虑优化。不难发现一点:如果我们扩展出来了 \(f(i)\) 并且决定选,那么我们一定要在这个子树中赚钱;而在 \(i\) 子树中赚钱的流程和之前求 \(f(i)\) 的流程是完全一致的,也就是说我们可以保留每一次求出 \(f(i)\) 后堆里面剩下的元素,如果扩展到 \(f(i)\) 就直接跳过中间的流程来到这里,并且 \(now\) 直接加上中间赚的钱数。用可并堆维护一下即可,复杂度 \(O(n\log^2 n)\)。
B [BalticOI 2022] Uplifting Excursion
这个题显然是一个背包问题,但是 \(l\) 比较巨大,无法按照传统方法做。
考虑一个经典的结论:我们优先放性价比最高的若干个物品,将体积调整到 \([l-m,l]\),然后再进一步调整到 \(l\)。在最后调整的过程中体积变化量是不超过 \(m^2\) 的。证明的话首先考虑,我们的体积最开始在 \([l-m,l]\),调整过程中一定有方案使得 \(S\in [l-m,l+m]\)。而这中间的每个值我们都只会经过一次,如果经过两次还更优的话我们在第一步贪心的时候就应该算上了。所以每个物品最多选 \(m\) 次,体积变化在 \([-m^2,m^2]\) 内。
于是我们只需要跑一个体积为 \(m^2\) 的多重背包即可,这样的复杂度就可以承受了。使用二进制分组优化后的复杂度为 \(O(m^3 \log m^2)\)。
C [CF771E] Bear and Rectangle Strips
首先有一个比较简单的 \(O(n^2)\) dp:设 \(dp(i,j)\) 表示第一行考虑到 \(i\) 列,第二行考虑到 \(j\) 列的最大矩形数。转移是比较容易的,提前预处理出每一个点向右合法的第一个扩展点即可做到 \(O(1)\) 转移。不过枚举的状态过多,无法通过。
考虑怎样优化状态数,我们有一个直觉是我们希望两行选的矩形尽可能均匀些,也就是说 \(i,j\) 不要相差过大。考虑先关注 \(dp(i, i)\),接下来我们断言:只有满足 \(dp(i,j)=dp(i,i)+1\) 或 \(dp(j,i)=dp(i,i)+1\) 的第一个 \(j(j\ge i)\) 是重要的,即我们只需要对它们向后转移即可。
感性理解起来非常容易,这个要求相当于每次只扩展一个矩形,显然扩展多个的情况是可以由扩展一个的情况推出的,这样的话就可以节省时间复杂度。显然每个 dp 状态的后继是 \(O(1)\) 个,最开始只有一种状态,所以总状态数是 \(O(n)\) 的。
D [CF1975G] Zimpha Fan Club
首先如果没有 *
此题是简单的,考虑怎样搞掉这个东西。不妨进行分类讨论。
发现如果两边都没有 *
就是平凡的情况,而如果都有其实也很平凡,这要求两个字符串第一个和最后一个 *
的左边和右边能够匹配,手玩后不难发现一定存在方案使得中间可以匹配上。
现在的问题就是怎样求只有一个字符串有 *
的问题了,首先还是先判断两边字符能否匹配,这样我们的第一个串就变成 *S*S*S*S*S*S*
这样的形式了。考虑我们实际上只需要求出每一个小的 \(S_i\) 串能否在 \(T\) 中匹配,如果可以就向后转移。而带通配符的字符串匹配可以使用多项式卷积来求,复杂度是 \(O(n^2 \log n)\) 的。
考虑这个做法不优在哪,我们每次都拿整个 \(T\) 串和 \(S_i\) 去匹配,实际上这不必要。我们只需要取当前的前 \(2|S|\) 个字符匹配,如果匹配失败那么就删掉前 \(|S|\) 个字符即可。这样我们就可以单次在 \(O(|S|\log |S|)\) 的复杂度内删掉至少 \(|S|\) 个字符,复杂度就是 \(O(n\log n)\) 的了。
不过此题的 \(n\) 有 \(2\times 10^6\),有点卡常,几个方法如下:
- 先全部转成点值表示,算完后再转回系数表示,这样可以少做两次 NTT。
- 处理 \(S_i\) 的系数时可以提前预处理。
E [CF1975H] 378QAQ and Core
考虑这个字符串核心的性质,不难发现,其一定是以该字符串的最大字符 \(c\) 为开头的一个后缀。
发现如果只有一个 \(c\),显然可以直接放在最末尾最优,此时核心就是 \(c\)。那么现在只需要考虑多个 \(c\) 的情况。不难发现这样一个性质:
- 答案字符串一定是形如
cScScS..Sc
的构造。原因在于如果第一个 \(c\) 前面或最后一个 \(c\) 后面还有字符,那么把它们挪到中间来一定更优。
于是我们每次在最后放一个 \(c\),剩下的就是若干个 \(c+S\) 的组合,我们对这些组合排序,把它们当成一个字符就可以递归解决问题了。现在的问题是怎样求出这些组合。
考虑贪心,这些组合的比较实际上并不是一般的字典序比较,而是在字符串末尾加上了一个 \(\infty\) 后再比较。我们希望这里面最大的尽可能小,首先给每个 \(c\) 后面都先给一个字符,接下来不是最大的已经不用考虑,为了让最大的尽可能小我们要继续往最大的后面加字符,所以每次只需要给最大的顺序加字符即可。
有一种特殊情况是字符数比 \(c\) 的数量少,此时一定会有 \(c\) 放在一起,为了尽可能小我们一定是平均分 \(c\),这样就会有多出来的几个 \(c\)。不过不用管,我们把这几个组合和多出来的 \(c\) 一起递归下去即可。
容易发现,这样做每次字符数至少减少一半,复杂度 \(O(n\log n)\)。
F [CF1975I] Mind Bloom
神秘 dp 题。
首先我们正难则反,考虑算出我们的手牌全部丢出的概率。我们先排除一些平凡的情况,然后剩下的情况满足 \(a_1=0,a_n>1,|S|\in [1,n-1]\),其中 \(|S|\) 是初始拥有的手牌集合。为了更好的刻画手牌之间的大小关系,钦定值相同的时候编号大的时候更大。
考虑怎样用尽可能少的状态刻画当前的手牌集合以计算答案,如果我们全部输掉,那么此时的 \(\max(S)\) 应该等于 \(0\),那么我们整个过程中的 \(\max(S)\) 的趋势应该是不断下降的,我们考虑以此为过程 dp。设 \(f(i,x,y)\) 表示当 \(\max(S)=i,|S-\{i\}|=x\) 的时候,后面第一次到达 \(\max(S)<i\) 的状态时 \(|S|=y\) 的概率。
考虑怎样进行这个转移。接下来需要明确一个小引理,后文会多次用到:从 \(n\) 个数中选 \(m\) 个,每个数被选中的概率为 \(\tfrac{m}{n}\)。考虑我们现在需要进行 \(\max(S)=i\to \max(S)< i\) 的转移,在这个过程中我们的 \(S\) 还会往上升,而这之中我们只关注 \(\max(S)\le n,n-1,n-2\cdots,i,i-1\) 的过程。
考虑设 \(g(j,y)\) 表示在 \((i,x)\) 状态下,第一次到达 \(\max(S)\le j\) 时 \(|S|=y\) 的概率。第一次打出 \(a_i\) 的时候有 \(g(n,x+a_i)=1\)。考虑 \(g(j,y)\) 的转移,我们需要向 \(g(j-1,*)\) 转移,需要讨论 \(j\) 手牌是否在手上。此时 \(\le j\) 的手牌共 \(j\) 张,牌堆中有 \(j-x\) 张,我们还能选 \(y-x\) 张,概率为 \(p=\tfrac{y-x}{j-x}\)。
- 如果 \(j\) 手牌不在手上,那么直接转移即可,\((1-p)g(j,y)\to g(j-1,y)\)。
- 如果 \(j\) 手牌在手上,打出 \(j\) 手牌然后进行转移,此时我们需要让 \(\max(S)\le j-1\) 且 \(|S|=q\)。不难发现这就是 \(f(j,y-1,q)\),转移为 \(pf(j,y-1,q)g(j,y)\to g(j-1,q)\)。
不难发现最后 \(f(i,x,y)=g(i-1,y)\)。
此时会发现一个问题,当 \(i=j,x=y-1\) 时,转移会变成 \(pf(i,x,q)g(j,y)\to g(i-1,q)\to f(i,x,q)\),显然出现了自我更新,所以需要额外解方程求出 \(f(i,x,y)\)。这一部分的复杂度是 \(O(n^5)\) 的。
现在考虑统计答案,设 \(h(i,x)\) 表示在前 \(i\) 张手牌中,除了初始已有手牌外,额外随机选了若干张手牌使手牌数达到 \(x\),输掉的概率。初始化即若 \(a_i=0\) 则 \(h(i,x)=1\)。考虑转移:
- 若 \(i\) 在初始手牌中,那么肯定要打出 \(i\) 手牌,利用 \(f\) 转移即可。\(h(i,x)=\sum f(i,x-1,y)\times h(i-1,y)\)。
- 若 \(i\) 不在初始手牌中,考虑计算它在 \(x\) 张牌中的概率。设 \(s_i\) 表示前 \(i\) 张手牌中初始有的手牌数,则 \(p=\tfrac{x-s_i}{i-s_i}\)。如果在选出的 \(x\) 张牌中,转移与上式一致,否则转移为 \(h(i,x)=(1-p)\times h(i-1,x)\)。
最终答案即为 \(1-h(n,s_n)\)。这一部分的复杂度是 \(O(n^3)\) 的。所以最终复杂度是 \(O(n^5)\),常数足够小可以通过。
G [CF1819D] Misha and Apples
考虑我们最后的答案肯定是一个连续后缀计算得出的,为此我们需要求出 \(n\) 之前最后一次清空的最早位置,然后计算答案就是简单的了。考虑怎样求,直接求比较困难,考虑递推求解。设 \(p_i\) 表示走到 \(i\) 之前最后一次清空的最早位置,\(f_i\) 表示走到 \(i\) 之后能否清空。
先考虑怎样求 \(f_i\),设 \(S_i\) 中所有元素在 \(i\) 之前最后一次出现的位置为 \(mx\),则:
- 如果 \(p_i<mx\),走到 \(i\) 的时候一定会被清空,\(f_i=1\)。
- 如果 \(mx\le p_i\) 且 \((p_i,i]\) 中有空集,我们一定可以通过调整空集使得其在 \(i\) 被清空,\(f_i=1\)。
- 否则 \(f_i=0\)。
然后考虑求 \(p_{i+1}\),先设 \(p_{i+1}=p_i\),则:
- 如果 \(p_i<mx\),为了让它尽可能早清空,我们先把 \(p_{i+1}\) 挪到 \(mx\) 处,进入第二种情况。
- 如果 \(mx\le p_i\),不断递增 \(p_{i+1}\) 直到 \(f_{p_{i+1}}=1\)。
然后我们就可以求出最终答案了。复杂度 \(O(n)\)。
H [CF1270G] Subset with Zero Sum
本题单里唯二做出来的题之一。
考虑 \(i-n\le a_i\le i-1\) 实际上相当于 \(1\le i-a_i\le n\)。考虑连边 \(i\to i-a_i\),这样每个点出度为 \(1\),必然构成一棵基环树。而基环树上的环正好满足每个点编号之和等于每个点指向的编号之和,即 \(\sum i=\sum i-a_i\),于是环上的点的 \(\sum a_i=0\),满足要求。
只需要实现基环树找环即可,复杂度 \(O(n)\)。
I [CF1810G] The Maximum Prefix
考虑前缀和的前缀最大值怎样维护,如果我们直接从前往后暴力维护,必然要维护当前前缀和 \(sum\) 和前缀和最大值 \(mx\) 两个状态,再加上枚举 \(i\),复杂度必然来到 \(O(n^3)\),不可承受。
考虑怎样优化状态,有一个经典的 trick 是反过来求最大值。我们考虑一个后缀的答案,也就是只考虑一段后缀的 \(S\) 的值,当我们新加上一个数 \(a\) 时,后缀的答案显然会变成 \(\max(S+a,0)\)。于是我们考虑以此设状态,令 \(f(i,j)\) 表示考虑 \([i,n]\) 时,\(S=j\) 的方案数。那么转移方程就是:
- \(p_i f(i+1,j)\to f(i,j+1)\)。
- \((1-p_i) f(i+1,j)\to f(i, \max(j-1,0))\)。
初值为 \(f(n+1,0)=1\),答案即为 \(\sum\limits_{i=0}^n h_i f(1,i)\)。这样做的单次复杂度是 \(O(n^2)\) 的。不过我们要对每个长度求一边答案,这样复杂度还是 \(O(n^3)\) 的。发现对于每一个长度,我们的转移方程是一致的,只是初值不同。那么我们就可以考虑反推贡献系数的 trick,设 \(g(i,j)\) 为 \(f(i,j)\) 在答案中的贡献系数,则 \(g(1,i)=h_i\),然后将上面的转移倒过来做一遍即可。复杂度就是 \(O(n^2)\) 的了。
J [CF2018B] Speedbreaker
对于一个城市,我们的最优策略应该是每一次向最小的 \(a_i\) 走,显然这样一定不劣。那么不难发现无解则代表有两个 \(\le a_i\) 的位置之间的距离超过了 \(a_i\),此时不管怎样走都不可能同时满足要求。
判掉无解的条件后我们就可以求答案了。首先观察出一个性质:答案一定是一段区间。证明的话考虑两个合法点 \(x,y\),如果中间有一个不合法点 \(p\),其走不到的点是 \(u\)。如果 \(u<p\),那么当 \(y\) 扩展到 \(p\) 时 \(y\) 就不合法了;否则 \(x\) 就不合法了。显然矛盾,所以性质正确。
然后再进一步发现,答案的区间正好是所有 \([i-a_i+1,i+a_i-1]\) 的交,因为按照上述策略走显然可行。于是我们就可以 \(O(n)\) 直接求出答案了。