2026 暑期模拟赛合集
2026.7 诸暨海亮集训
思维碰撞
给定一个序列 a,求随机选择序列中的一个连续区间,这个区间中相同数组数的期望值,\(n\le 10^5,a_i\le2^{32}-1\)。
暴力:枚举区间端点 l,r,每次向右拓展新的 r 的时候,之后每次需要增加的贡献会增加 \(cnt[a[r]]\),时间复杂度 \(O(n^2)\)。-------------------------------68分
考虑如何优化。
我们可以先对序列进行离散化,把值域转化为与 n 同阶,然后枚举 x,预处理出 x 出现的所有位置 \(p_{x[i]}\),那么数字 x 对最终答案的贡献就是 \(\sum_{i<j} p_{x[i]}(n-p_{x[j]+1})\)。
这个式子可以化简成 \(\sum_{j=2}^k[(n-p_j+1)\sum_{i=1}^{j-1}p_i]\),其中 \(\sum_{i=1}^{j-1}p_i\) 可以运用前缀和快速计算,只需遍历一次 j 即可,时间复杂度 \(O(n)\)。------------------------100分
断桥日出
对一个序列长度为 n,进行以下 m 次操作(\(n,m\le 10^5\)):
0 l r x:区间 \([l,r]\) 乘上 x1 l r求区间 \([l,r]\) 的和欧拉函数和欧拉函数:\(\phi(n)\) 为 1~n 与 n 互质的数的个数。
暴力:每次修改、询问 \(O(n)\) 地进行操作,时间复杂度 \(O(n^2)\),可以通过 \(n,m\le 1000\)。------------------------30分
对于没有修改的数据,只有查询区间和,考虑运用线段树,先预处理出每个数的欧拉函数值,再用线段树区间查询即可。------------------------40分
接下来,考虑数 a 乘上 b 后,\(\phi (a)\) 会发生什么变化,打表欧拉函数结果如下:
1 1
2 1
3 2
4 2
5 4
6 2
7 6
8 4
9 6
10 4
11 10
12 4
13 12
14 6
15 8
16 8
17 16
18 6
19 18
20 8
我们发现,当 \(a\mod b=0\) 时,\(\phi(ab)=\phi(a)*b\),当 \(a\mod b≠0\) 时,\(\phi(ab)=\phi(a)*(b-1)\),观察 \(a_i=x=2\) 的部分分,发现总是满足第一种情况,所以线段树的修改操作,可以直接替换为区间乘 x。-------------------55分
考虑如何让线段树实现两种情况。我们可以在线段树上维护当前区间是否满足每个数都有因子 \(p\),因为通过 \(x\le 100\) 的数据范围中,我们意识到 100 中的质数只有 25 个,所以因子的种类也只有 25 个,可以直接用二进制状压,在线段树中记作mask。
那么,在修改操作中,我们可以先对 x 进行质因数分解,使得每次修改操作都乘上质数,然后在线段树上走的过程中,如果走到的区间所有数都包含这个质因子,就不用向下走了,直接打上标记即可,最终复杂度 \(O(25n+n\log n)\)。-------------------100分
长夜启明
高楼有 n 层,每层是有 k 个点的多边形,其中有 m 盏是关键的,必须点亮,其余可点亮可关闭,每一层相邻的两盏灯不能同时打开,相邻两层相同位置的等不能同时打开,求总开关方案数模 998244353 的结果。
\(n\le10^{18},m\le 50,3\le k\le 11\)
先考虑 n 较小的情况,我们发现 k 始终很小,考虑状压 DP,把 k 个灯的开关状态压缩成二进制数,一层一层进行转移。-------------------35分
当 n 较大时,考虑如何转移,容易想到矩阵乘法。
为什么?先不考虑关键的灯,我们可以得到一个转移矩阵,即枚举所有的单层状态,大约 \(2^{11}=2048\) 个,这个矩阵的 i 行 j 列记录状态 i 和状态 j 相邻是否合法,设合法的单层状态为 G,那么这个矩阵有 G 行 G 列。
转移一次,只需进行一次矩阵乘法,考虑如何快速进行大量转移。尝试倍增,每次向上跳 \(2^i\) 层,我们先预处理出跳 \(2^i\) 层需要乘的矩阵,也就是转移矩阵的 i 次方,然后我们只需要一次乘法就能跳一次,矩阵乘法复杂度 \(O(G^3)\),倍增最多跳 \(\log n\) 次,可以通过本题。-------------------60分
现在就差如何处理关键灯,很简单,关键灯很少,所有我们一段一段倍增,每次遇到有关键灯的层单独处理,注意要预处理出每一层关键灯的集合。
最终复杂度 \(O(G^3\log n+mG^2\log n)\)。-------------------100分
游戏
有 n 种游戏,第 i 个价格为 \(c_i\) 元,你第 0 天有 a 元钱,每天(不含第 0 天)可以获得 b 元钱,每天至多购买 1 种游戏,求买完所有游戏所需的最小天数。
显然,反正每天获得的钱都固定,如果你能买尽量就买,因为一天不能买多个,所以我们把游戏的价格排序,从小到大地贪心购买,复杂度 \(O(n)\),数据较弱。
中位数
定义一个含 m 个数列的中位数为把数列从小到大排序后的第 \(m/2+1\) 个数(向下取整)。
求一个长度为 n 的序列中 \(n(n+1)/2\) 个子区间中中位数所构成数列的中位数。
部分分:直接最暴力的枚举、排序,复杂度 \(O(n^3\log n)\)。-------------------------------30分
用队顶堆优化,每次移动 r 时更新答案,复杂度 \(O(n^2)\)。-------------------------------60分
正解:本题是经典套路,考虑二分答案,若答案是 mid,那么它是最终答案需要满足有 \(n(n+1)/2/2+1\) 个区间求出的中位数比 mid 大,这样 mid 在最终序列中才处在中间位置。
考虑如何求有多少个中位数比 mid 大,这里也用一个经典的转化,设比 mid 大的数为 1,比 mid 小的数为 -1,那么一个区间的中位数大于等于 mid 就等价于这个 0-1 区间的和大于等于 0。
我们考虑求出这个 0-1 数列的前缀和 s,发现区间和大于等于 0,又等价于 \(s_{l-1}\ge s_r\),所以我们可以建一个权值树状数组,把值存到下标中,存的内容是该下标的数量,枚举一次原数组,每次查询 \(1~a_i\) 中树状数组的和,也就是满足上面式子的个数,之后把新枚举的值加入树状数组,方便后续查询。
时间复杂度 \(O(n\log^2n)\)。-------------------------------100分
秋游
有 n 个未知的正整数 aᵢ(≤ bᵢ),分别去掉每个数后,剩下 n-1 个数的 gcd 会得到 n 个值。问在所有可能的 aᵢ 中,这 n 个 gcd 最多能有几种不同的数。
n 个数的 gcd 实际上就是分别分解质因数,求每个质数指数最小数的乘积。
最优的构造方式如下:
- 所有出现过的质因子是最小的 k 个质数(2, 3, 5, 7, 11, …)这是显然的,不然容易超出 \(b_i\) 的限制。
- 有一个数包含所有这些质因子(乘积最大)
- 另外 k 个数,第 i 个数缺少第 i 个质因子(也就是乘积去掉一个质数)这样才能保证第一条。
我们发现,当 \(k>15\) 时,前 k 个质数的乘积 \(2*3*5*...\) 已经超过 long long,所有只需要枚举 k 在 1 到 \(\min(n-1,15)\) 之间,判断是否能满足 \(b_i\) 的限制即可。
构造出来的数列也就是 \(S,S/2,S/3,S/5,...\),共 \(k+1\) 项,其中 S 为前 k 个质数的积。
那么如何判断?我们贪心,把构造出来的 a 和给出的 b 排序,依次匹配,就是最有可能的方案。
复杂度 \(O(n\log n+nk)\)。-------------------------------100分
叠叠乐
长度为 n 的序列 a,进行以下操作:
- 选择一个 i,对任意 \(i<j\le n\),将 \(a_j\) 变为 \(a_j+a_i\)。
最多使用 \(3n\) 次操作,使序列单调不增或单调不降,并保证任意时刻 \(|a_i|\le10^{18}\)。
\(n\le10^5,|a_i|\le n\)
先考虑当 a 都是正数时怎么做,发现对于所有的 \(a_i>a_{i+1}\),对 i 进行一次操作,就能让 \(a_i<a_{i+1}\),扫一次 a,最多 n 次操作就能让 a 单调不降。负数同理。
当有正有负的时候,我们考虑能否统一符号,我们对第一个非 0 数 \(a_x\) 进行 \(n-1\) 次操作,发现当 \(a_x>0\) 时,剩下的所有数必定变为正数,再用上述方法扫一遍即可,当 \(a_x<0\) 时同理,剩下的数会被减成负数,之后让序列单调不增即可。-------------------------------40分
但是,我们发现这样构造出的数会很大,因为每次操作都是不断累加的,可能会超过 \(10^{18}\),不符合题意。
那我们反过来,完成符号统一后从右往左,就能避免不断的累加的问题。-------------------------------100分
往事回首
长度为 n 的
+-序列,每个符号代表快乐/悲伤,会让心情值 +1/-1,给定 q 次询问,每次询问忽略掉区间 \([l,r]\),求心情值有多少不同的值。\(n,q\le2*10^5\)
部分分
暴力枚举。-------------------------------30分
当 \(r=1\) 时,遍历一次,同时记录心情值的种类即可。-------------------------------40分
正解
每次 +1/-1 变化,可以发现最终所有出现过的心情值是一段连续的区间,我们只需要求出这个区间的最大/最小值即可。
设心情的前缀和为 s,记录:
lmin[i]:s[0,...,i]的最小值;lmax[i]:s[0,...,i]的最大值;rmax[i]:s[i,...,n]的相对最小值;rmax[i]:s[i,...,n]的相对最大值;- 这里的“相对”就是 \(s[j]-s[i-1]\) 的值,即偏移量。
那么区间 \([l,r]\) 的最小值为
最大值为
这里对右半部分的处理为加上左边结束后的结果。
复杂度 \(O(n)\)。-------------------------------100分
组队难题
\(2n\) 名选手,要两人一队组成 n 支队伍,成绩最高的 \(2n-1\) 人,没人选一个成绩比自己低的,自己最讨厌的人,需要保证每个人不和这个选出的最讨厌的人在一组,求总方案数对 998244353 取模。\(n\le 2000\)
考虑容斥原理,设 \(f(i)\) 为指定有 i 对矛盾的人的方案个数,剩下的人随意,那么最终答案即为
我们发现,讨厌的关系是个树形结构,因为一个人只会讨厌成绩比自己差的人,所以如果一个人 i 讨厌 \(p_i\),那么就让 \(p_i\) 是 i 的父亲,这个树的根是谁都不讨厌的 \(2n\)。
考虑如何求出 \(f(i)\),方案数可以拆成 指定 i 对矛盾的方案数*其他人随意组队的方案数。
对于指定 i 对矛盾的方案数,可以用树形 DP 解决:
dp[u][j][0] : 以 u 为根的子树中,选了 j 对“讨厌关系”组队,且 u 还没有被配对
dp[u][j][1] :以 u 为根的子树中,选了 j 对“讨厌关系”组队,且 u 已经被配对(跟某个子节点组队了)
dfs 遍历一次树,考虑把 u 的孩子 v,把子树 v 合并进子树 u 该如何转移:
-
当 u 和 v 不组队,我们枚举 j1,j2 为之前有讨厌关系的数量,注意枚举的时候最大值是 \(siz/2\),这样每个结点会被枚举 1 次,复杂度才正确:
new_dp[u][j1 + j2][0] += dp[u][j1][0] * dp[v][j2][0] new_dp[u][j1 + j2][0] += dp[u][j1][0] * dp[v][j2][1] new_dp[u][j1 + j2][1] += dp[u][j1][1] * dp[v][j2][0] new_dp[u][j1 + j2][1] += dp[u][j1][1] * dp[v][j2][1] -
当 u 和 v 组队,因为我们只讨论有讨厌关系的组队,那么必然有 \(p_u=v\) 或 \(p_v=u\),需要提前判断,而且原来必须都是未组队状态:
new_dp[u][j1 + j2 + 1][1] += dp[u][j1][0] * dp[v][j2][0]
最终这一部分的方案数量为 dp[2n][i][0]+dp[2n][i][1]。
那么剩下的队伍是可以随意组队的,还剩下 \(2n-2i\) 个人未组队。
考虑 \(2n\) 个人随意组队的方案数 \(s_n\),这个值应该是 \(\frac{C_n^{2n}\times n!}{2^n}\)。因为我们选 n 个“队长”,共有 \(C_n^{2n}\) 种选法,剩下的人任意排列,有 \(n!\) 种,但因为这样每个相同的组会被计算 \(2^n\) 次,所以要除掉。计算 s,需要预处理阶乘、阶乘逆元、组合数、2的幂次的逆元。
最终,\(f(i)=dp[2n][i][0]+dp[2n][i][1]\times s(n-i)\)。
复杂度是树形 DP 的 \(O(n^2)\)。-------------------------------100分
图染色
给定一张 n 个点 m 条边的无向图,你需要为每个顶点染色,使得:
- 对于任意一种颜色,不存在只由它构成的环;
- 对于任意两种在图中出现过的颜色,存在只由这两种颜色构成的环。
你可以只使用一种颜色,而且染上同一种颜色的所有节点不需要联通。
要求线性。
首先不存在环时,全都染一种颜色即可。-------------------------------20分
先考虑一种绝对正确的染色方法:
任意顺序枚举所有点,一个点不可以染成颜色 x 当且仅当这个点是一个环上的一点,且这个环的其它点的颜色都是 x。这样如果染色 x 就违反了第一个条件。
尝试把这个点染上颜色 1,2,3,...,直到合法为止,如果没有颜色可以,就要新增一个颜色。
新增的颜色为何满足条件 2?因为我们增加一个颜色的时候,必须满足这个点在之前所有颜色的环中,所有对于新颜色和任意的旧颜色,刚好有一个环。-------------------------------40分
考虑如何实现,我们用一个并查集,维护不同颜色的所有连通块,那么一个点所在环的其他颜色颜色相同,等价于这个点与一个颜色的连通块有两条边相连,每当决定一个点的颜色后,遍历这个点的所有出边,合并并查集即可。忽略并查集复杂度,复杂度 \(O(n+m)\)。-------------------------------100分
林波舞
有 n 根横杆,第 i 根横杆一开始的高度为 \(a_i\) 单位,移速为每秒 \(v_i\) 单位(正数代表向上,负数代表向下)。杆子的活动范围为 0~\(10^9\) 单位,当它尝试向这个范围之外移动时会停止不动。
你决定将这些横杆按照某种顺序依次摆放在离起点 1~n 单位处,让表演者依次穿过这些横杆,必须匀速。
找出一个尽可能小的数 h 使得:存在一种摆放方式,使得不论表演者选择怎样的前进速度,他穿过横杆时对应横杆高度的最小值都不大于 h。\(n\le10^5\)
不难想到二分答案,考虑如何 check。
部分分给了一些提示:当 \(v_i\ge 0\) 时,显然舞者越慢越好,对于 \(v_i>0\) 的柱子舞者都可以等无限长时间等过去,答案即为所有 \(v_i=0\) 柱子的高度最小值。-------------------------------5分
当 \(v_i<0\) 时,显然舞者越快越好,所有她在一瞬间移动过去,答案为所有柱子初始高度的最小值。-------------------------------25分
假设我们知道一个柱子的位置,以及高度的最小值 h,那么这可以将舞者的速度锁定在一个范围中。
- 当 \(v>0\) 时,舞者移动的越慢,走到那里时柱子的高度越高,所以能确定一个速度的最大值。而且把柱子放到位置 1 有机会让舞者通过时高度最低。由 \(a+vt=h\),可得 \(t=\frac{h-a}{v}\),由 \(v_{r}=\frac{s}{t}\) 得 \(v_r=\frac{v}{h-a}\)。
- 当 \(v<0\) 时,舞者移动的越快,走到那里时柱子的高度越高,所以能确定一个速度的最小值。而且把柱子放到位置 n 有机会让舞者通过时高度最低。由 \(a-|v|t=h\),可得 \(t=\frac{h-a}{|v|}\),即 \(t=\frac{h-a}{-v}\),由 \(v_{l}=\frac{s}{t}\) 得 \(v_l=\frac{-vn}{a-h}\)。特殊地,如果一个向下移动的杆子的初始高度就小于 h,那么舞者是不可能挑出速度的,直接合法。
- 当 \(v=0\) 时,如果高度 \(a<h\),那么舞者也是不可能挑出速度的,直接合法。
把所有柱子都放到 1/n 试一次,可以求得范围 \(maxl<v'<maxr\)(\(v'\) 是舞者的速度),如果 \(maxl\ge maxr\),覆盖所有实数,说明舞者不能选出一个 \(v'\),让最低高度大于 h。
注意二分的精度问题,时间复杂度 \(O(n\log V)\)。-------------------------------100分
卷
给定 n 个非负整数数组 a,b ,求
\[\sum_{i=1}^n\sum_{j=1}^n⌊\sqrt{|a_i-b_j|}⌋ \]\(n\le 10^6\),\(\sum a_i,\sum b_i\le 10^7\)。
算法一:暴力枚举 \(a_i,b_j\),复杂度 \(O(n^2)\)。-------------------------------30分
算法二:对于 \(a_i,b_i\le 10^3\) 的数据,统计每个 \(a_i,b_j\) 的出现次数,对于每组 \(a_i,b_j\) 累加贡献:\(cnt\times ⌊\sqrt{|a_i-b_j|}⌋\)。-------------------------------50分
算法三:注意到 \(\sum a_i,\sum b_i\le 10^7\),发现不同的 \(a_i,b_i\) 很少,当 \(a_i\) 为最小的 n 个整数时,\(\sum a_i=(n-1)n/2\),所以不同的 \(a_i,b_i\) 是 \(O(\sqrt{\sum a_i+\sum b_i})\) 级别的,故我们对 a,b 进行离散化,暴力枚举 a,b,每次像算法二那样计算贡献,复杂度 \(O(\sum a_i+\sum b_i)\)。-------------------------------100分
代码比较懒,用 map 多了一个 log。
王牌对王牌
小洛和机器人玩卡牌游戏,牌堆共有
n张牌(n为偶数),牌堆顶是“先摸到的牌”,三种卡牌:
- 学习(1):直接得 1 分。
- 偷卷(2):立即从牌堆额外摸 2 张牌(如果牌堆还有牌)。
- 反转(3):取消上一张“偷卷”的效果;如果对“反转”用,则恢复再前面一张牌的效果。
规则:
- 双方初始无手牌,每回合开始时各自摸 2 张牌,轮流进行。
- AI 机器人只会出“学习”和“反转”:自己回合尽可能多出“学习”;对方出牌后,如果手中有“反转”会立即打出。
- 小洛只关心自己能获得的最大分数,不关心 AI 的得分。
求小洛能获得的最大分数。
\(n\le 300\),n 为偶数。
算法一:(与正解无关)观察题意,需要决策的只有何时出偷卷,对于学习,获得之后什么时候打出的效果都是一样的,对于反转,在对方反转我们的偷卷是打出即可。
所以我们暴力 DFS 每次是否使用偷卷,然后进行模拟,取最大得分即可,可以获得 \(n\le 10\) 的部分分。-------------------------------20分
算法二:本题显然可以尝试 DP,先考虑特殊性质 \(a_i\le 2\),即没有反转,记 \(dp[i][j][0/1]\) 表示当前牌堆的前 \(2i\) 张牌已经被拿走,玩家有 j 张偷卷时,0/1 表示轮到玩家还是 AI 行动,此时可以获得的最大得分。
显然有转移
其中 a,b 表示牌堆上方两张牌的学习、偷卷数量。
两个式子分别是摸牌、用偷卷牌的两个转移。(注意第二行的偷卷操作是在摸牌之后操作的,所以从状态 1 转移)
注意为什么只转移一次?因为第一维的状态是按出牌数量设定的,如果连续出偷卷,那么剩下的转移,将交给之后转移。-------------------------------40分
算法三:(与正解无关)加入反转后怎么做?先考虑一种最暴力的 DP,把什么都记下来:
记 \(dp[i][j][k_1][k_2][0/1]\),其中 \(k_1,k_2\) 分别为玩家和 AI 此时有多少反转。
因为有反转,所以玩家出偷卷牌不被抵消的条件为 \(k_1\ge k_2\),消耗完偷卷牌后双方分别剩余 \(k_1-k_2,0\) 张偷卷,时间和空间复杂度为 \(O(n^4)\)。-------------------------------80分
算法四:注意到我们现在记录的状态有些多余,考虑如何优化掉一维。
记 \(dp[i][j][k][0/1]\) 为表示当前牌堆的前 \(2i\) 张牌已经被拿走,玩家有 j 张偷卷,玩家偷卷+反转数量,加上 AI 反转数量为 k 时,0/1 表示轮到玩家还是 AI 行动,此时可以获得的最大得分。
为什么这样设置 k?因为玩家如果想摸牌,就要拼命打偷卷和反转,以应对对方的反转,所以如果 玩家偷卷+玩家反转-对方反转>1,那么在消耗之后,玩家就还有至少 1 张偷卷牌,也就是 \(k>1\) 时可以转移。这样就把判定能否偷卷的条件从两维优化到一维。
考虑消耗之后 j,k 的变化,不算摸牌,\(j=\min(j,k)-1,k=k-1\)。
最终转移式如下:
其中 a,b,c 表示牌堆上方两张牌的学习、偷卷、反转数量。
三个式子分别表示玩家摸牌、使用偷卷、AI 摸牌。
实现细节:
- k 有可能是负数,所以要设置一个偏移,使下标不为负数。
- 目前的 dp 数组空间开不下,第一维要设置为滚动数组。
时间复杂度 \(O(n^3)\),空间复杂度 \(O(n^2)\)。-------------------------------100分
博物馆
给定一棵 n 个点的树。你需要在树上放置若干条路径(两个端点之间的最短路径),要求:
覆盖所有点和所有边:每个点和每条边都至少被某条路径“看到”(路径经过的点/边即被看到)。
路径两两不相交:任意两条路径不能有公共点。
最小化路径数量:在满足上述条件的前提下,用的路径越少越好。
计数:在路径数量最少的方案中,有多少种不同的方案(方案不同当且仅当存在一条路径在一种方案中出现而在另一种中没有)。
\(n\le 5\times 10^5\)
部分特殊性质 B 的菊花图很好做,答案为 0 0。-------------------------------5分
树形 DP,记 \(dp[u][0/1][0/1/2]\) 为以结点 u 为根的子树,结点 u 在/不在 链上,结点 u 在链上的度为 0/1/2(为 0 时说明这个链只有 u 一个结点,为 1 时说明 u 是这个链的端点,为 2 时说明 u 是这个链中间的一点)。实际上,第二维为 0 时,第三维必定为 0。
考虑如何转移,设 u 为 v 的父亲,通过限制每个点到链的长度不超过 1 可得,如果 u 不在链上,那么它的所有子树 v 必定被链覆盖,否则连接 u,v 的边将不会被看到,所以 u,v 至少有一个在链上,转移时也就至少有一个第二维是 1。
所以,我们枚举 u,v 的两个状态(在不在链上/链上度数),如果至少有一个在链上,就可以把子树 v 中的最小值加到 u 中,和之前的值取 min。
但是有一个特殊的情况,就是如果 u,v 都是链的端点(第三维为 0/1),就可以连接两个链,把两边的链合成一个经过结点 u,v 的链,转移之后点 u 的度数会 +1,链的数量因为合并,会减少 1。
最终答案即为 \(dp[1]\) 中的最小值。
考虑如何统计方案,在 dp 转移的过程中同步处理即可,做的是乘法,注意让链的数量最小是优先的,不能直接对方案数取 min/max,要依着方案数最小,代码中写了一个 upd 函数用来更新。-------------------------------100分
作业
你一共要完成 n 科作业,其中第 i 科作业有 \(a_i\) 道题目,每道题目只会属于一个学科。
每一天你会从还没完成的题目中等概率地随机选择一道,然后将这道题所属的学科的所有题目都完成。
求出学科 1 被完成的天数的期望。
\(n\le 10^5\)
我们只需要分别求出第 2,...,n 排在学科 1 之前完成的概率,加起来再加 1 就是学科 1 完成时间的期望。
只考虑两个学科,他们完成的先后关系与其它学科无关,即学科 i 比学科 1 先完成的概率为 \(\frac{a_i}{a_1+a_i}\)。
最终答案即为
-------------------------------100分
小h学步
有 n 组 d 维向量,每组 n 个且和为 0。现在从每组选出 1 个向量加起来,问 \(n^n\) 种不同选择下,求和后的向量的长度平方总和是多少。答案对 998244353 取模。
\(n\le 100,1\le d\le 3\),坐标绝对值始终不超过 100(数据水,正解是 \(O(n^2d)\),可以加强数据范围)
暴力做法:直接枚举 \(n^n\) 种选择,按题意模拟出最终坐标(就是 \(\sum x,\sum y,\sum z\)),答案为
可以通过 \(n\le 8\) 的数据。-------------------------------40分
考虑变换上面的式子,我们发现,
因为所有人移动之后都会到原点,所以最终的 \(x=0\),即 \(\sum_{i=1}^nx_i=0\)。
代入上面的式子得
因为每个向量会被选 \(n^{n-1}\) 次,所以上面的式子结果为
y 和 z 坐标是同理的,那么最终答案
复杂度 \(O(n^2)\)。-------------------------------100分
小球进洞
给定 n 个结点的树,根为 1,每个叶子结点有一个洞,给定 m,在这个树的随机的边上挖一共 m 个洞,一条边上可能会有多个洞,挖在每条边上的概率都为 \(\frac{1}{n-1}\)。
每个非叶子结点上有一个小球,小球向叶子的方向滚,每次走到结点,小球可以选择它子节点中的一个移动,经过边上的一个洞,小球可以选择是否进入洞中,如果走到叶子结点,就会落到叶子结点的洞中,一个洞可以有多个小球,两个方案不同当且仅当有一个小球掉入不同的洞中,输出可能的方案数模 998244353。
\(n\le 5000,m\le10^5\)
我们发现,一个小球能够选择洞的数量为以这个小球为跟的子树中洞的数量,最终答案就是每个小球能选择数量的乘积。
对于测试点 3,\(m=0\),洞只在叶子结点,故我们可以用 \(leaf[u]\) 表示以 u 为根的子树包含多少叶子结点,dfs 的过程中累加答案即可,最终答案即为所有 \(leaf[u]\) 的乘积。-------------------------------10分
对于测试点 4,我们只需要枚举唯一一个在边上的洞,每次重新 dfs 合并一次,把所有答案的和除以 \(n-1\) 就是期望,需要求逆元。-------------------------------20分
下面考虑一种朴素的 DP:(与正解无关)
记 \(dp[u][i]\) 为以 u 为根的子树中,确定使用了 i 个边上的随机洞的方案数。在 dfs 时,设 u 是 v 的父亲,考虑如何把以 v 为根的子树插入到 u 中,我们需要枚举第二维随机洞的数量,有转移式:
时间复杂度 \(O(nm^2)\)。-------------------------------50分
尝试优化这个 DP,我们发现 \(n<m\),球比洞少很多,所以我们尝试从枚举洞变成枚举球。
记 \(dp[u][i]\) 为以 u 为根的子树中,子树中的球进入到了 i 个不同的随机洞中,的方案数。可以通过乘法原理把 v 合并进 u:
合并之后,我们还需要考虑结点 u 上的小球,会掉进哪个洞。
-
掉进原有的洞:原来一共有 \(leaf_u+i\) 个洞,掉进任意一个都是新方案,答案乘上这个数量:
\[dp[u][i]\times(i+leaf[u])→dp[u][i] \] -
掉进新的洞:掉进的这个洞之前没有球掉进去,所有要从 i 少 1 的位置来转移,因为新开的洞是随机的,它必须在以 u 为根的子树中才能让小球 u 掉进去,在 u 子树的概率是:
u子树中的边/总共的边,即 \(\frac{sz[u]-1}{n-1}\),转移如下,实际需要处理逆元。\[dp[u][i]+(dp[u][i-1]\times\frac{sz[u]-1}{n-1})→dp[u][i] \]
最终答案即为:
对于每个 i,究竟是哪 i 个洞用到是不确定的,方案数一共 \(C_{m}^{i}\),\(i!\) 是因为确定哪几个洞后,还需要确定它们对应的位置,也就是全排列 \(i!\) 种方法。
时间复杂度:枚举状态 \(O(n)\),对于转移,每两个结点只会在 LCA 处合并一次,共有 \(O(n)\) 次,总时间复杂度 \(O(n^2)\)。-------------------------------100分
合并
有 n 个数 \(1,2,...,n\),每次选择两个数 \(x,y\),擦掉他们变成 \(\frac{x+y}{2}\),重复操作直到只剩一个数 a,求当 a 取最大值时 \(a\times 2^{n-1}\) 对 \(10^9+7\) 取模的值,可以证明答案为整数。\(n\le 10^{10}\)。
显然,一个数越早被合并,就要被除以越多次数的 2,要让结果最大,就要先合并最小的数,所以合并的顺序就是先合并前两个数,得到的数再和第三个数合并,以此类推,考虑算出每个数对答案的贡献。
分析或尝试样例可以发现,前两个数会被除以 \(n-1\) 次 2,之后的第 i 个数会被除以 \(n-i+1\) 次 2(显然第二个数也适用这个式子),那么最终的答案就是:
如果直接算,时间复杂度是 \(O(n)\) 的,需要继续优化。-------------------------------40分
分块打表
注意到 \(10^{10}\) 不是很大,本地跑十几秒就能跑完,故我们可以把每 \(10^7\) 个答案记录下来,写到代码里,这样测试点的询问距离我们之前算过的最多只有 \(10^7\),是可以通过的,表的长度也只有 1000。
-------------------------------100分
最短路
给定一个 n 个点 m 条边的无向图。
假设一条路径上的边权依次为 \(w_1,w_2,…,w_k\),定义这条路径的长度为 \(\sum w_i+\min \{w_i\}-\max \{w_i\}\)。
分别求出在这个定义下从 1 出发到 2,3,…,n 的最短路径的长度,不连通输出 -1。
\(n,m\le 2\times 10^5\)
对于 w 都相等的数据,直接跑最短路/BFS即可。-------------------------------20分
这种路径长度在最短路中不好计算,考虑将其转化。
这种路径长度实际就相当于:在图中选择一条边把边权翻倍,再选择一条边把边权设为 0,操作之后的最短路长度。显然为了让路径最短,我们会选最短边翻倍,答案加上了 \(\min \{w_i\}\),把最长的边设为 0,就减去的 \(\max \{w_i\}\),这样的最短路刚好就是我们要求的。
到这里已经很显然了,我们在分层图上跑最短路。我们把图分成四层,具体含义如下:
- 0 还没有进行翻倍、清零操作
- 1 只进行过清零操作
- 2 只进行过翻倍操作
- 3 两种操作都进行过
每个结点都分成这四种状态,最终结点 1 到 结点 u 的答案就是点 \(1_0\) 到点 \(u_3\) 的最短路。考虑如何进行操作,即在不同状态间转移。
对于每条连接 u,v 的边,边权为 w,我们需要建这些边
在 u0--v0、u1--v1、u2--v2、u3--v3 建边权为 w 的无向边:不进行任何操作
在 u0->v1、v0->u1、u2->v3、v2->u3 建边权为 0 的有向边:进行清零操作
在 u0->v2、v0->u2、u1->v3、v1->u3 建边权为 2w 的有向边:进行翻倍操作
这样就成功构造了分层图,然后跑 dij 即可。
时间复杂度 \(O(n\log n)\)。-------------------------------100分
花海摄影
给定长度为 n 的序列 \(a_i\),m 次询问,每次给定 x,求把序列 a 的每个数异或 x 后的逆序对数量。\(n,m\le10^6\)
算法一:对于每次询问,修改原数组后枚举所有的 \((i,j)\) 对,找出所有逆序对并计算数量,时间复杂度 \(O(n^2m)\)。-------------------------------20分
算法二:优化逆序对个数的求法,可以用归并排序、树状数组等算法进行优化,时间复杂度 \(O(nm\log n)\)。-------------------------------52分
算法三:我们如果从左到右枚举一次序列,每枚举到一个数,这个数对逆序对的贡献就是前面所有比这个数大的数的个数。
因为每次进行异或运算,所有我们考虑在二进制下如何比较大小,显然两个数大小取决于它们第一个不同的二进制位,那么枚举到一个数的贡献,就等于“对于这个数二进制中的每个 1,前面二进制对应位为 0,且更高位都相同的数的数量之和”。
显然这可以用字典树快速查询,每次把数插入字典树,记 \(a_k\) 为对于所有 \(i<j\),满足 i 二进制的第 k 位为 0,且 j 二进制第 k 位为 1,且更高位都相同的 \((i,j)\) 对数量,反之,记 \(b_k\) 为对于所有 \(i<j\),满足 i 二进制的第 k 位为 1,且 j 二进制第 k 位为 0,且更高位都相同的 \((i,j)\) 对数量。
如果不对数组进行异或操作,那么答案就是 a 数组之和。
那么如果对数组进行异或操作,我们可以把异或数 x 转化为二进制,原来相等的位异或之后一定还相等,影响的只有是 10 比较还是 01 比较,对于第 k 位的 1,我们把 \(a_k\) 和 \(b_k\) 交换,就达到了异或的效果,此时答案会变化 \(b_k-a_k\),时间复杂度 \(O(n\log n)\)。-------------------------------100分
罚时
比赛有 n 道题,第 i 题需要 \(a_i\) 的时间写。
你的策略是先随机选 k 个题读,然后重复以下步骤 n 次:从读过的题中选择一个时间最短的完成,如果还有没读的题就再读一道题。
若你以 \(p_1,p_2,...,p_n\) 的顺序完成题目,那么第 i 题的罚时 \(t_i=\sum_{j=1}^ia_{p_j}\),求总罚时 \(\sum_{i=1}^nt_i\) 的期望对 \(10^9+7\) 取模。
数据由代码随机生成,\(n\le 2\times 10^6\)。
本题部分分少,直接看正解。
随机的东西只有读题的顺序,只要确定顺序就对应唯一的罚时,最终答案为所有 \(n!\) 种读题顺序所计算出的罚时除以 \(n!\)。
将 \(a_i\) 从大到小排序,记 \(dp[i]\) 为完成前 i 题所有方案的总罚时。
-
当 \(i<k\) 时,我们可以注意到因为要读 k 道题,前 \(k-1\) 道题一定会放到最后完成,因为这些题的时间是最大的,之后读新题一定会有更优的选择,所有这些题会留到最后,会累积前面所有题目的罚时,所以有转移
\[dp[i]=i!\times\sum_{j=1}^{i}j\times a_j \]其中 \(i!\) 为这 i 道题的顺序可以随便排列,贡献都相同,后面的 \(j\times a_j\) 是因为前面的题会按时间从小到大完成,第 j 道题会为后面时间比自己大的每道题贡献 \(a_j\) 个罚时,比他大的就是位于 a 数组前面的,加上自己的罚时就是 j 次罚时。
实际上,因为我们后续要考虑从 \(dp[i-1]\) 转移到 \(dp[i]\),所以我们只在乎 \(dp[k-1]\) 的值,知道他就能完成后续转移,显然有
\[dp[k-1]=(k-1)!\times\sum_{j=1}^{k-1}j\times a_j \]可以 \(O(n)\) 计算。
-
当 \(i\ge k\) 时,考虑如何从 \(dp[i-1]\) 转移到 \(dp[i]\),我们把贡献拆成三部分:
-
一、前 \(i-1\) 道题互相之间产生的罚时
这部分是不变的,但因为加入一个数,读题顺序的方案数由 \((i-1)!\) 变为 \(i!\),所以这部分的总贡献要乘上 i,贡献 x 为
\[x=i\times dp[i-1] \] -
二、它及比它晚完成的题目所增加的罚时
插入一道题,之后的题目都要加上这段时间的罚时,考虑计算它及后面题目的总增加量 y。
设题目 i 是第 j 道被读的(j 有可能是 1~i),那么题目 i 是第 \(\max(1,j-k+1)\) 道完成的。因为 a 数组是从大到小排序的,新加入的时间总是最短的,所以如果在读完 \(k-1\) 题后读了这道题,那么这道题一定会在下一轮立刻被完成,所以是 \(j-k+1\),如果这个值是负数,就取 1。
对于它及比它晚的所有题目,和这道题的距离为 \(i-\max(1,j-k+1)+1\),这就是后面题需要吃本题罚时的数量,乘上时间长度,再乘上前 \(i-1\) 个数任意排列的方案数,就是后面所有题增加的罚时
\[b=(i-1)!\times a_i\times \sum_{j=1}^i(i-\max(1,j-k+1)+1) \]这个式子看起来是 \(O(n)\) 的,但是它可以优化,我们后面再说。
-
三、前 \(i-1\) 道题目对第 i 道题目罚时的影响
题目 i 本身是有罚时的,它自身时间产生的罚时已经在第二部分计算了,但前面的题目对它的罚时还没有计算。
显然,只有第 k 到 \(i-1\) 个题又可能出现在题目 i 的前面,且题目 i 必须在这些题之后被读,不然就依据时间最短优先选题目 i 了。
即设 \(p_x\) 为前 \(i-1\) 道题中第 x 个被完成的,若其中的题目 x 对 题目 j 的罚时产生影响,当且仅当 \(j>x+k-1\)。其中 \(x+k-1\) 为题目 x 是第几个被读的(加上最初读的题),这个次序需要在 j 之前。
考虑 j 的数量,又因为 \(j\le i-1\),所以这种情况下可能的 j 有 \(i-(x+k-1)\) 个(相减即可得),这部分的总罚时为
\[c=\sum_{一种读题顺序}\sum_{x=1}^{i-(k-1)}(a_{p_x}\times(i-(x+k-1)))① \]这个式子的复杂度是非常高的,但我们能发现它和 \(dp[i-1]\) 存在巧妙的关系,依据 dp 数组的定义,
\[dp[i-1]=\sum_{一种读题顺序}\sum_{x=1}^{i-1}(a_{p_x}\times(i-x))② \]注意到两式有相当一部分是相同的,把 ② 拆开
\[dp[i-1]=\sum_{一种读题顺序}(\sum_{x=1}^{i-(k-1)}(a_{p_x}\times(i-x))+\sum_{x=i-(k-1)+1}^{i-1}(a_{p_x}\times(i-x)))③ \]用 ③-①,等式右边为
\[\sum_{一种读题顺序}(\sum_{x=1}^{i-(k-1)}(a_{p_x}\times(k-1))+\sum_{x=i-(k-1)+1}^{i-1}(a_{p_x}\times(i-x)))\\ =\sum_{一种读题顺序}((k-1)\sum_{x=1}^{i-(k-1)}a_{p_x}+\sum_{x=i-(k-1)+1}^{i-1}(a_{p_x}\times(i-x)))④ \]\(\sum_{一种读题顺序}\sum_{x=1}^{i-(k-1)}a_{p_x}\) 为除了前 \(k-1\) 题外的所有题的时间和,可以转换为 \((i-1)!\times \sum_{y=k}^{i-1}a_y\)。
\(\sum_{一种读题顺序}\sum_{x=i-(k-1)+1}^{i-1}(a_{p_x}\times(i-x))\) 为时间最大的 \(k-1\) 个题的时间和乘上一个 \(1,2,3,...\) 的系数,可以转化为 \((i-1)!\sum_{y=1}^{k-1}y\times a_y\)。
把这两个转化代入到④式中,得
\[(k-1)(i-1)!\times \sum_{y=k}^{i-1}a_y+(i-1)!\sum_{y=1}^{k-1}y\times a_y⑤ \]最终的贡献 c 实际上就是 \(dp[i-1]\) 减去⑤式(和②-①的右侧是等价的)的结果,所以
\[c=dp[i-1]-(k-1)(i-1)!\times \sum_{y=k}^{i-1}a_y-(i-1)!\sum_{y=1}^{k-1}y\times a_y \]直接计算是 \(O(n)\) 的,还是之后再考虑优化。
这三部分加起来,就是 \(dp[i]\) 的值,即
\[dp[i]=a+b+c \]转移的次数是 \(O(n)\) 级别的,所以现在的复杂度是 \(O(n^2)\)。-------------------------------70分
我们需要继续优化,让每一次转移都做到 \(O(1)\),现在的问题在于如何快速计算 \(b,c\)。
对于 b,我们发现 \(\max(1,j-k+1)\) 取 1 时当且仅当 \(j\le k\),所以我们把这个 min 拆开,得
\[b=(i-1)!\times a_i\times \sum_{j=1}^i(i-\max(1,j-k+1)+1)\\ =(i-1)!\times a_i\times (\sum_{j=1}^k(i-1+1)+\sum_{j=k+1}^{i}(i-(j-k+1)+1))\\ =(i-1)!\times a_i\times (\sum_{j=1}^ki+\sum_{j=k+1}^{i}(i-j+k))\\ =(i-1)!\times a_i\times (ki+(i-k)i-\frac{(i-k)(i-k+1)}{2})\\ =(i-1)!\times a_i\times (i^2-\frac{(i-k)(i-k+1)}{2})\\ \]这个式子现在可以 \(O(1)\) 计算了。(预处理阶乘)
对于 c 中的这一项 \(\sum_{y=k}^{i-1}a_y\),我们可以预处理 a 的前缀和,就可以用差分 \(O(1)\) 计算,对于 \(\sum_{y=1}^{k-1}y\times a_y\),我们可以直接预处理形如这样的式子,前面 \(i<k\) 的转移正好也能用到。
这样,c 就可以 \(O(1)\) 计算了。
因为有 \(O(n)\) 次转移,所以整个 \(i\ge k\) 的转移是 \(O(n)\) 的。
-
总时间复杂度 \(O(n)\)。-------------------------------100分
2026.8 大连京体集训
织星
给定 u,v,令 \(k=2u+v\),有 \(2k\) 个点,编号 \([1,u],[k+1,2k-u]\) 为绯色,\([u+1,k],[2k-u+1,2k]\) 为青色。
初始有 \(k\) 条边:\(i\) 连 \(k+i\)。随机补 \(k\) 条新边,每个点与一个新边相连,每条边必须连接一绯一青。求最终图中连通块数量的期望。
\(u,v\le 10^6\)。
本题的部分分不太好写,直接看正解。
最初每个点的度为 1,然后加边又保证了一个点与一条边相连,所有最终每个点的度都是 2,所以图的最终形态是若干个环。
但是在补充边的过程中,图是由一堆链慢慢连接成了一个环,每条增加的边都连接了两个链,并又可能成环。
对于补边过程中的每个链,有以下几种种类:
- 红红边:链的两个端点都是红色
- 蓝蓝边:链的两个端点都是蓝色
- 红蓝边:一个端点是红色,另一个是蓝色
最开始的时候,有 \(k\) 个红红边,\(k\) 个蓝蓝边,接下来我们要加 \(2k\) 个红蓝边,所以任何时刻的红红边和蓝蓝边的个数都相等。
设 \(f(a,b)\) 为图中还剩 a 个红红边、a 个蓝蓝边、b 个红蓝边时,补全所有边后的期望连通块(环)数量。
每次随机连接一条边,实际上是连接了两条链,考虑每条边对链状态的影响。
- 当选择的两条链分别为红红、蓝蓝时,新链是红蓝链,a 减少 1,b 增加 1。
- 当选择的两条链分别为红红、红蓝时,新链是红红链,a 不变,b 减少 1。
- 当选择的两条链分别为蓝蓝、红蓝时,新链是蓝蓝链,a 不变,b 减少 1。
- 当选择的两条链都是红蓝链时,分为以下情况:
- 如果这两条链是不同的,那么会产生一个新的红蓝链,但减少两个,所以 b 减少 1。
- 如果这两条链相同,那么可以直接成环,同样 b 减少 1,但连通块数量会增加 1。
考虑如何用 \(f(a,b)\) 转化为 \(f(a,b-1)\)。(后三种情况)对于特定的一条红蓝链,它自己首尾相连的概率为 \(\frac{1}{2a+b}\),如果选中了,它自己收尾相连,成环后连通块个数 +1,否则连通块数量不变,这两种情况都能让 b 变成 b-1,于是递推式为
这样就能推出 \(f(a,0)\)。我们已知的是最终情况:\(f(0,0)=0\),所以考虑如何递推 a。
由上面的情况一可知,\(f(a,0)=f(a-1,1)\),等式右边的 \(b=1\),我们用刚才推出的式子转化为 \(f(a-1,0)\):
这样就能成功递推 a 了。
把上面的递推式展开,得到:
第一个求和是递推 b,第二个求和是递推 a。
因为有 u 个红红链/蓝蓝链,剩下的 v 条边一定都是红蓝链,所以最终答案为 \(f(u,v)\)。
可以直接求这个式子,时间复杂度 \(O(n)\)。(100分)
本题还可以用调和级数优化时间复杂度,但是这个时间复杂度足够通过本题。
精钥
给定长度为 m 的字符串 a,b,选出 \(i_1,i_2,...,i_r(0\le r\le m)\),使 \(a_{i_1}a_{i_2}...a_{i_r}+b_{i_1}b_{i_2}...b_{i_r}\) 的字典序最大,求这个字符串。\(m\le 50\)(数据较水)特殊性质:\(a=b\)。
算法一 暴力枚举:直接枚举每一位选/不选,时间复杂度 \(O(2^n)\)。(30分)
算法二 特殊性质:特殊性质时,只需要选所有 \(a_i\) 最大的即可。(50分)
算法三 区间DP:考虑区间 DP,记 \(dp[i][j][k]\) 为只考虑区间 \([i,j]\),选 k 个位置能组成的字典序最大的字符串。
- 如果不选位置 l,那么有转移 \(dp[l][r][k]=dp[l+1][r][k]\)。
- 如果选位置 l,那么考虑从 \(dp[l+1][r][k-1]\) 转移,记这个字符串为 t,这个 t 实际上是由 \(a+b\) 两部分组成的,所有需要把他拆开,记为 \(x,y\),那么 \(dp[l][r][k]=a[l]+x+b[l]+y\)。
初始化所有 \(k>0\) 的 \(dp[l][r][k]=\#\),表示不可达,最终答案为 \(dp[0][m-1][k]\) 的最大值。(100分)
回响
有一个排列 p,由数 \(1,2,...,2q+1\) 组成,进行 \(q+1\) 次操作,每次把最中间的数加入集合 C,然后任选一个数删除,求集合 C 的子集 D,满足排序后相邻元素差为 1(例如 \(\{4,5,6\}\)),的最大 \(|D|\)。
\(q\le 2\times 10^5\)。特殊性质:\(p_i=i\)。
算法一 暴力:纯暴力,可以通过 \(q\le10\)。(10分)
算法二 转化:注意到第一次操作,必定拿走第 \(q+1\) 个数,直接把它删掉,不用讨论。
剩下的数分成了两部分,\(p_1,p_2,...p_q\),\(p_{q+2},p_{q+3},...,p_{2q+1}\)。之后的每次操作,会先删除一个数,发现如果删左边,那么取右边最靠左的,如果删右边,那么取左边最靠右的,那么每次操作可以转化为:取左边或者右边最靠近中间的,然后删掉另一边的一个数。
考虑最终的集合 D 为区间 \([l,r]\)(这个先暴力枚举),如何判断是否合法。显然,\([l,r]\) 这些数是关键的,不能被删掉,剩下的随便。
考虑从右到左枚举左边,同时从左到右枚举右边,在每个时刻,如果两边都枚举了前 i 个数,那么必须满足 \(关键数数量\le i\),因为每次将关键数加入集合,都需要删掉一个无关的数,如果大于就没有无关数抵消了。
所以对于 \([l,r]\) 是否合法,可以枚举所有的 i,判断是否小于等于 i 即可,每次扩大 i 的时候更新关键数数量,时间复杂度 \(O(n^3)\)。(40分)
算法三 双指针优化:注意到如果 \([l,r]\) 合法,那么更容易的 \([l+1,r],[l,r-1]\) 必定合法,也就是具有单调性,所以枚举 \(l,r\) 可以用双指针代替,时间复杂度 \(O(n^2)\)。(70分)
算法四 线段树优化:接下来只需考虑如何快速判断区间是否合法。
我们记 \(val[k]\) 表示左右前 k 个位置的 \(k-关键数数量\),那么合法的条件就变为了对于所有 \(k\le i\),有 \(val[k]\ge 0\),可以转化为 val 的最小值大于等于 0,这个容易想到用线段树维护。
考虑如何修改。当 r 向右时,区间扩大,数字 r 会变为关键数,找到数字 r 所在位置 pos,那么对于所有 \(k\ge pos\),\(val[k]\) 都会因新增关键数而减少 1。
当 l 向右时,区间缩小,数字 l-1 会不再是关键数,找到数字 l-1 所在位置 pos,那么对于所有 \(k\ge pos\),\(val[k]\) 都会因减少关键数而增加 1。这样每次移动指针的修改和查询都可以在线段树上查询了(区间加+全局最小值)。
时间复杂度 \(O(n\log n)\)。(100分)
潮痕断讯
长度为 n 的十进制字符串 S,校验参数为 d。
把 S 划分成若干个连续片段,对于每一个片段,能被 d 整除的合法,否则不合法,不存在连续两个片段不合法的划分是安全的。
求安全划分数模 \(10^9+7\)。
\(n\le 10^5,d\le 10^6\)。
特殊性质:\(\gcd(d,10)=1\)。
算法一:朴素 DP
先考虑一种朴素的 DP,记 \(dp_{i,0/1}\) 为前 i 未数进行划分,最后一个区间是非法/合法的方案数。我们可以暴力枚举 j ,通过区间 \([j,i]\) 进行转移。
- 当 \([j,i]\) 合法时,\(dp[i][1]+=dp[j][0]+dp[j][1]\)。
- 否则 \(dp[i][0]+=dp[j][1]\),上一段必须合法。
对于判断一个区间是否合法,我们可以先枚举 j 再枚举 i,每次 i 向右移动时把 val 变成 \((val*10+s[i])\mod d\),判断是否等于 0 即可。
初始化 \(dp[0][1]=1\),答案为 \(dp[n][0]+dp[n][1]\)。
时间复杂度 \(O(n^2)\)。(30分)
算法二:特殊性质 \(\gcd(d,10)=1\)
考虑继续优化,这次不能枚举 j,但是特殊性质可以提示我们做法。
我们记 \(suf[i]\) 为字符串第 i 位及以后组成的后缀模 d 的结果,可以从后往前用 \(suf[i]=suf[i+1]+10^{n-i}\times s[i]\) 计算。
那么区间 \([j,i]\) 表示的数,等价于 \((suf[j]-suf[i+1])/10^{n-i}\),需要让这个数模 d 为 0。
我们发现当 \(\gcd(d,10)=1\) 时,这个式子等价于 \(suf[j]\equiv suf[i+1](\mod d)\),先考虑特殊性质即互质的情况。
这个时候我们枚举 i,每次向右扩展时,可以把 \(j=i\) 对于的 dp 值 \(dp[i-1]\) 加入桶的 \(suf_i\) 的位置,我们需要两个桶 x,y,一个存 \(dp[i][0]+dp[i][1]\),一个存 \(dp[i][1]\),这样就有转移:
- 合法:正好就是上述的第一个桶 \(x[suf[i+1]]\)。
- 非法:答案为 \(\sum_{j=0}^idp[i-1][1]\)(上一个必须合法,这玩意在循环时累加),减去 \(y[suf[i+1]]\)(因为最后一个必须非法,要减去所有合法的匹配)。
这样单次转移是 \(O(1)\) 的,总时间复杂度 \(O(n)\),可以解决特殊性质,代码如下:(50分)
算法三:对不互质的情况转化
考虑不互质的情况,我们尝试把 d 转化成互质的情况,可以拆成 \(2^a+5^b+m\),把 10 的因数 2,5 都分出去,最后保证 \(\gcd(m,10)=1\)。
对于每个区间的转移,分成两种情况:
- 当区间长度 \(>20\),所表示的数会 \(>10^{20}\),注意到 \(d\le 10^6\le2^{20}\le5^{20}\),所以此时区间一定能被 \(2^a\times 5^y\) 整除,我们只需要判断 m 是否整除,又因为 \(\gcd(m,10)=1\),所以可以用特殊性质的方法开桶来计算答案,所有转移是 \(O(n)\) 的。
- 当区间长度 \(\le 20\),可以像暴力 dp 那样暴力转移,需要的次数很小。考虑复杂度,对于每个 i,最多有 20 个短区间,再暴力转移一次,需要 \(20^2\),所以所有转移是 \(O(400n)\) 的。
此时加上多测(T=100)的常数,常数约为 \(4\times 10^4\),会超时。
进一步优化,可以边枚举 j 边更新短区间暴力转移的区间数 val,这样短区间每次转移是 \(O(20)\),最终总时间复杂度 \(O(n)\),常数约为 \(2\times 10^3\),需要卡常。(100分)
次钥留声
给定 b,n,输出满足以下条件的第二大的数:
- 转化成 b 进制不存在两位的数字相同。
- 是 n 的倍数。
无解输出 -1。
\(n\le 10^{18},b\le 12\)。所以有解的数据随机生成。
本题的正解虽然是折半搜索,但是注意到数据随机生成,所以这里介绍一种神奇的普通爆搜可以卡过。
算法一 朴素暴力:直接暴力搜索转化成 b 进制后的结果。
先枚举 len,再枚举排列情况,判断得到的数是否是 n 的倍数,然后取第二大的。
时间复杂度 \(O(b\times b!)\)。(50分)
算法二 \(n\ge10^5\) 特殊性质:通过 \(b\le 12\),能构造出的最大数约为 \(11^{12}\) 量级,那么 n 最多取到 \(\frac{11^{12}}{10^5}\) 的倍数,这个是可以枚举的。
所以枚举 n 的若干倍,分解成 b 进制看是否合法即可。(60分)
算法三:暴力优化:考虑对算法一进行优化,如果我们从大到小枚举了len,枚举全排列时也从大到小枚举,这样每次得到的数都是当前最大的,直接找到第二大的就可以跑路了。
搜索到第 \(10^7\) 之后如果没有就直接判无解,因为有解的数据是随机的,WA 的概率特别低。
时间复杂度 \(O(10^7)\),正确率极大。(100分)
潮歌初现
一个无限长的序列 a,每个元素独立等概率地取自 \([1,m]\),给定长度为 n 的数列 b,求 b 第一次作为 a 的连续子段出现时结束位置 x 的期望。
\(n,m\le 2\times 10^5\),对 998244353 取模。
结论:答案为
证明:考虑从左到右枚举 b,每次在 \(b_i\) 的位置加入一名赌徒,初始拥有 1 元,每一名赌徒要赌若干轮之后退场。
每次向右枚举时,前 i 个赌徒会押数组 a 的下一个数等于 \(b_{i+1}\),如果押对了,赌徒的资金乘 m,猜错归零。
那么在首次匹配完成后,还在场上的赌徒所猜的数恰好就是 b 数组的一个 border(最长公共前后缀),他们剩余钱数总和即为 \(\sum_{p\in P}m^p\),因为整个数组一定是他的 border 所以这个式子和上面的答案是等价的。
因为每次赌徒有 \(\frac{1}{m}\) 的概率让钱翻 m 倍,那么他获得钱的期望永远是 1 元钱,钱的总数也就是赌徒数量的期望,也就是匹配时结束的位置。
于是跑一遍 KMP 求 border,然后通过快速幂计算即可。
时间复杂度 \(O(n\log n)\)。
人鱼和鸣
长度为 n 的数列 a,每个元素在 \([1,m]\) 中,求有多少个子区间,满足子区间中 \(1,2,...,m\) 的出现次数相同。(如果没有出现视为出现 0 次)
\(n,m\le 10^6\)。
算法一:直接保留枚举区间,用桶记录每个数的出现次数,判断是否是合法区间,时间复杂度 \(O(n^2)\) 或 \(O(n^3)\)。(40分)
算法二:用一个桶记录 \(c_{i,j}\) 为前 i 个数中 j 的出现次数,但是发现不好操作。我们考虑差分,这样增加一个数就只需要修改两个位置的差分数组,记 \(d_{i,j}=c_{i,j}-c_{i,j+1}\)。这样当读入 \(a_i=x\) 时,只需要讲 \(d_{i,x}\) 增加 1,将 \(d_{i,x-1}\) 减少 1。
注意到如果一个差分数组相同,那么这一段每个数出现次数必定相同,问题转化为找到 \(d_i=d_j(i\ne j)\) 的个数。
考虑对 \(d_i\) 进行哈希,可以开一个 map,每次新加入时将答案增加之前的数量,并更新数量,时间复杂度 \(O(n\log n)\)。(100分)
珊瑚光庭
给定长度为 n 的序列 a,请你构造长度为 n 的 01 序列 b,要求 01 个数相同,并最大化
\[\sum_{i=2}^{n}a_i(b_i⊗b_{\frac{i}{2}}) \]其中 ⊗ 表示两个操作数相同为 1,不同为 0,\(\frac{i}{2}\) 向下取整。
\(n\le 450\),n 为偶数。(数据水)
算法一:保留枚举所有情况,复杂度 \(O(2^n)\)。(10分)
算法二:考虑把式子进行转化,发现这是一个二叉树的结构,根节点为 1,结点 u 的左右儿子分别是 \(2u,2u+1\),这样一个结点 u 能贡献 \(a_u\) 当且仅当它的父节点和它在 01 序列中对应的数相同。
具体地,考虑树形 DP,记 \(dp[u][k][b]\) 为以 u 为根的子树,有 k 个 1,且点 u 在 01 序列对应 b 的最大答案。
对于结点 u,按照如下步骤转移:枚举左右子树的中 1 的个数分别为 i,j(都小于等于各自的 siz),然后再枚举 b 以及左右子树的 bl,br,有
注意要取 max,叶子结点初始化 \(dp[u][0][0]=dp[u][1][1]=0\),其他为 -inf,答案为 \(\max(f[1][n/2][0],f[1][n/2][1])\)。
每次转移复杂度 \(O(sz_u^2)\),总复杂度 \(O(n^2)\)。(100分)
群落余量
一个图有 n 个点 m 条边,每个点有点权,n 次操作每次删除一个点和与之相连的边,每次删除后求点权最大的连通块的点权。\(n\le 10^5\)。
算法一 暴力查找:每次删除所有边之和,在图上跑一遍找到最大的连通块,时间复杂度 \(O(n^2+m)\)。(30分)
算法二 反向开点:并查集非常擅长维护连通块,但删点操作不好处理,考虑倒着处理每次操作,那么就相当于每次加一个连通块,最开始只有一个点,然后遍历要加的所有边,进行并查集 merge 即可,时间复杂度 \(O(n+m)\)。(100分)
扩增层级
定义
\[f(n, k) = \sum_{x_1=0}^n \binom{n}{x_1} \sum_{x_2=0}^{x_1} \binom{x_1}{x_2} \cdots \sum_{x_k=0}^{x_{k-1}} \binom{x_{k-1}}{x_k} \]求
\[\sum_{i=0}^n f(f(n, i), i) \]对 998, 244, 853 取模。
考虑最里面的求和
依据组合的意义,这相当于从 \(x_{k-1}\) 中选几个都行,那么化简为
取 \(x_{k-2}=2,3,4\) 代入,发现 \(\sum_{x_{k-1}=0}^{x_{k-2}} \binom{x_{k-2}}{x_{k-1}}2^{x_{k-1}}\) 分别等于 \(9,27,81\),找规律,显然后面两项可以化简为 \(3^{x_{k-2}}\)。
严格的证明需要二项式定理在这里不写了。
根据规律,显然再往前分别是 \(4^{x_{k-3}}...\),那么
我们要求
把 f 的化简结果代入,得
考虑直接用快速幂求这个式子,对于每个 i 要先算 \(i^n\),这个数是很大的,如果我们求的时候取模,结果就错了,因为这个玩意要作为指数再参与一次乘方。
一种暴力的方法是这样累加的,时间复杂度 \(O(n^2)\)。(60分)
int x=i;
for(int j=1;j<=n;j++)x=qpow(x,i)%mod;
ans=(ans+x)%mod;
正解的计算方式是运用费马小定理,因为模数是质数,那么
所以用快速幂进行第一次乘方时可以取模数 \(p=mod-1\),这样就保证模 mod 意义下大小不变,答案就对了。
时间复杂度 \(O(n)\)。(100分)
标签相容
我们规定一个包含小写字母和问号的字符串为一个参数化单词。
如果两个参数化单词中,可以用特定的小写字母将其中的问号进行替换,并最终使得两个单词完全相同,那么原来的两个参数化单词就被称为是相似的。例如,
a???和?b?a都可以替换为abba,因此a???和?b?a是相似的。给定 N 个长度为 M 的参数化单词,求有多少对相似的参数化单词。
\(n\le 5\times 10^4,m\le 6\)。
考虑在加入一个字符串的同时,加上这个字符串和之前字符串的贡献。
我们考虑对字符串进行哈希,这样来处理 ?:
- 状态 0~25 表示这一位是字母
a~z; - 状态 26 表示这一位本来是字母,但是因为问号可以替换为任意字母,所以这里填一个问号表示字母;
- 状态 27 表示本来就是问号。
插入时枚举每一个字符:
- 如果是字母,有两种情况:
- 就填原来字母,加上状态 0~25;
- 换成问号也行,加上状态 26。
- 如果是问号,只能加上状态 27。
查询时同理,但是有区别:
- 如果不是问号,可以就是原本的字母 0~25;
- 如果是问号,可以是原本是字母被换成问号的 26;
- 上面两组情况都可以用问号 27 应付,因为问号可以换成任意的字符。
插入/查询时每得到一种情况的哈希值,可以存到 map 中。
时间复杂度 \(O(n\log V)\)。
实现细节:
- 查询时,先看看 map 中有没有这项,否则如果直接查即使没有也会申请空间,最后会 MLE。
- 有 27 种状态,每一轮的哈希要乘大于 27 的数。(100分)
共鸣轨道
有 n 个点,第 i 个点权为 \(a_i\),对于每两个点 \(i\le j\),若 \(\gcd(a_i,a_j)>1\) 就把 \((i,j)\) 连有向边,规定 \(\gcd(0,0)=0\),给定 q 次询问,判断 \(x,y\) 是否可达。\(n,q\le 10^5,a_i\le 10^3\)。
算法一:输入后枚举所有点对,计算 gcd 判断是否可以连边,每次询问从起点开始搜索,能找到终点说明连通,时间复杂度 \(O(n^2+nq)\)。(80分)
算法二:注意到 \(a_i\) 很小,发现每个数最多只会有 4 个不同的质因子。
记 \(go[i][p]\) 为结点 i 下一个具有质因子 p 的结点编号,那么 \((x,y)\) 可达等价于存在一个 r 的质因子 p 使得 \(go[x][p]\le y\)。
考虑如何求 go,我们可以先预处理出 1000 以内的所有质数(168 个),还有所有 \(a_i\) 的所有因子,然后从右到左递推求,对于每一个数 \(a_i\),如果存在质因子 p,那么有 \(go[i][p]=i\)(依据定义),否则对于每一个 q 都就可以赋值为 \(go[i][q]=go[las[p]][q]\),也就是从上一个递推。
有几个特殊情况
- \(l=r\) 时答案为 Yes;
- \(l=1\) 或 \(r=1\) 时答案为 No;
- \(l=0\) 或 \(r=0\) 时,判断是否存在 \(l\le k\le r\) 使得 \(a_k>1\),如果存在就是 Yes。
(100分)
终幕结界
给定一棵有 n 个节点的树。节点 i 的布置代价为 \(c_i\),保护半径为 \(d_i\);每条边有一个正整数边权。两点之间的距离定义为它们之间唯一简单路径上所有边权之和。
请选出一个节点集合 S,在每个节点 u 处至少存在一个 \(v\in S\),使得 \(dist(u,v)\le d_u\)。
选出节点 v 需要支付 \(c_v\)。请最小化总费用。
本题有丰富的子任务,这里介绍四个。
算法一 \(n\le 20\)
每个点有选/不选两种可能,直接暴力枚举所有可能,对于每种可能判断是否合法,时间复杂度 \(O(2^n\times n)\)。(15分)
算法二 树为一个菊花
按是否选 1 分类讨论。若 1 选择,则所有的其它点 u 满足 \(d[u]<dist(1,u)\) 的要选,求和 \(c_u\) 加上 \(c_1\) 即可。
如果不选择 1,那么设 x 为 \(dist(1,x)\) 最小的点,那么所有的 \(d[x]<dist(1,x)+dist(1,u)\) 的点都够不到,要选。
取最优的即可,时间复杂度 \(O(n^2)\)。(30分)
算法三:\(d_i=w_i=1\)
记 \(dp[u][0/1/2]\) 为以 u 为根的子树,靠子树保护/靠父亲保护/靠自己保护 的最小花费。有转移:
- \(dp[u][0]=\sum\min(dp[v][1],dp[v][2])\)(靠子树,子树不能靠父亲)
- \(dp[u][1]=\sum\min(dp[v][1],dp[v][2])+\max(0,\min(dp[v][2]-dp[v][1]))\)(靠子树,子树不能靠父亲,还要花费是孩子由靠父亲转为靠自己)
- \(dp[u][0]=\sum\min(dp[v][0],dp[v][1],dp[v][2])+c[u]\)(选自己,怎么都行)
答案为 \(\min(dp[1][1],dp[1][2])\)。时间复杂度 \(O(n^2)\)。(45分)
算法四:\(w=1\),且所有 \(c_i\) 相等
所有边权和点权都相等,所以考虑贪心,按 深度-限制距离 从大到小考虑所有点,若当前点限制仍未满足,将其 d 级祖先染色,同时标记其能满足哪些点的限制,时间复杂度 \(O(n^2)\)。(65分)
算法五:正解
记 \(f[u]\) 为搞定以 u 为根的子树的最小花费。
对于每个点,考虑离它最近的点,这两个点称为相互配对的点。显然,一个点的所有配对点最多有一个不在以这个点为根的子树中,因为如果上面有两个点,那么距离必定不同。所以可以考虑记录配对点。
还是树形 DP,记 \(dp[u][j]\) 为搞定以 u 为根的子树,且 u 和 j 配对的最小花费。显然,\(f[i]=\min(dp[i])\)。
考虑转移,对于 \(dp[i][j]\):
-
若 \(dist(i,j)>d[i]\),那么是无法配对的,是无效状态,直接设为 inf。
-
否则考虑 u 的每个儿子 v:
- 若 v 子树的配对集合全在子树内,贡献为 \(f[v]\);
- 若 v 子树的配对集合除了 j 点都在子树内,贡献为 \(f[v]-c[j]\)。(减去重复算的 \(c[j]\))如果不是 j 点,则子树外多余 1 个配对点,不是最优。
所以转移方程为
\[dp[u][j]=c[j]+\sum\min(f[v],dp[v][j]-c[j]) \]
答案为 \(f[1]\),时间复杂度 \(O(n^2)\)。(100分)
纤维续光
给定两个长度为 n 的子序列 a,b,选出一个 a 的子序列,对于选出的长度为 k 的子序列 a',要求对于任意的 \(1\le j\le k\) 有 \(a'_{j+1}>b_ja'_j\),求最大的长度 k。\(n\le 10^6\)。
算法一 \(n\le 1000\) 暴力
进行这样一个 DP,记 \(dp[i][j]\) 为前 i 个数,选的最后一个数是 j 的上升子序列的最长长度。
- 不选第 i 个数时 \(dp[i][j]→dp[i+1][j]\)。
- 选第 i 个数时 \(dp[i][j]+1→dp[i+1][i+1]\),前提是 \(a_{i+1}>b_{dp[i][j]}\times a_j\)。
答案是 \(\max\{dp[n]\}\),时空复杂度 \(O(n^2)\)。(50分)
算法二 另一种 DP,\(b_i>1\) 的部分分
记 \(dp[i][j]\) 表示前 i 个数,选出长度为 j 的上升子序列,最后一个数的最小值。
- 不选第 i 个数时 \(dp[i][j]=dp[i-1][j]\)。
- 选第 i 个数时 \(dp[i][j]=a[i]\),前提是 \(a_{i}>b_{j-1}\times dp[i-1][j-1]\)。
我们发现当 \(b_i>1\) 时,每次都至少增长 2 倍,所以答案最高是 \(O(\log a_i)\) 的,总时间复杂度 \(O(n\log a_i)\)。(65分)
算法三 最长上升子序列,\(b_i=1\)
显然就是最长上升子序列问题,维护最终序列,每次用二分找出第一个大于插入数的位置并替换,是当前最大的就插入,最终答案为最后序列的长度,时间复杂度 \(O(n\log n)\)。(80分)
算法四 优化
注意到对于每个 \(dp[i]\),它的值都是随 j 单调递增的,因为构造的序列越长,最优时结尾的数一定越大。
考虑 \(dp[i-1]\) 何时需要转移到 \(dp[i]\),发现有很大一部分的转移是没有意义的:
- 当 \(dp[i-1][j]<a_i\) 时,此时如果满足条件则 \(dp[i][j]=\min(dp[i-1][j],a[i])=dp[i-1][j]\),dp 值一定不会变化,没有意义;
- 记第一个 \(dp[i-1][j]\ge a_i\) 的位置 j 为 k,那么对于所有的 \(j>k+1\),那么注意到条件转化后:\(a_i>dp_[i-1][j-1]\times b_{j-1}>dp[i-1][k]\times b_{j-1}\ge dp[i-1][k]\ge a_i\) 一定不成立,所以一定不能转移。
那么只有 \(dp[i-1][k]\) 需要转移,所以第一维可以不用开,扫一遍 a,对于每一个 \(a_i\) 记录 dp 数组第一个大于等于它的位置 k,然后判断位置 k 是否可以转移并转移即可,每次需要二分,时间复杂度 \(O(n\log n)\)。(100分)
双环刻时
定义 \(\phi(x,y)=((x-1)d+y-1)\mod w+1\)
给定 m,d,w,a,b,求有多少对 \(1\le x,y\le\min(m,d)\) 满足 \(\phi(x,y)=a,\phi(y,x)=b\)。
\(m,d,w\le 10^9,a,b\in[1,w]\),w 是质数。
子任务 1 \(m,d\le 1000\)(30分)
子任务 2 \(w\le 10^5\)(30分)
子任务 3:数据随机生成(20分)
算法一:直接暴力枚举所有 \((x,y)\),时间复杂度 \(O(\min(m,d)^2)\)。(30分)
算法二:题面中从 1 开始不好看,下文中 a,b 为实际的 \(a-1,b-1\)。
依据题意,一个 \((x,y)\) 合法需要满足
其中 \(x,y\in[0,\min(m,d)-1]\)。
一种暴力的方式就是枚举 x,然后解关于 y 的同余方程并记录解的数量,但是复杂度 \(O(w\log w)\),可以通过 \(w\le 10^5\) 的特殊性质。
算法三:考虑对这个方程组消元,对 ① 式移项得 \(y\equiv a-dx\),代入 ② 式得 \(d(a-dx)+x\equiv b\),即
若 \(d^2-1\ne 0\) 时,可以求出它的逆元 inv,求出 \(x=(da-b)\times inv(d^2-1) \mod w\),这就可以求出方程的一组特解 \(x_0\)。因为 \(x\equiv x_0\),我们就可以计算出 x 的个数。
同样,我们可以看出 y 的个数,最终答案就是 \(cnt_x\times cnt_y\)。
这里假设了 \(d^2\equiv 1\pmod w\),随机数据是可以避免这种情况的。(50分)
算法四
设 \(n=\min(m,d)-1\)。
考虑 \(d^2\equiv 1\pmod w\) 时,有以下两种情况
-
\(d\equiv-1\pmod w\)
代入到最原始的方程组,得
\[y-x\equiv a\pmod w\\ x-y\equiv b\pmod w \]显然 \(a\equiv-b\pmod p\),如果不成立一定没有解。
如果有解,设 \(u=y-x\),问题就转化为求 \(u\equiv a\pmod w\) 的 \(0\le x,y\le n\) 的个数,依据这个范围,可得 u 的取值范围 \(-n\le u\le n\)。
考虑函数 \(y=x+u\),那么函数必须在 \(0\le x,y\le n\) 的正方形中,所以 x 的取整范围为
\[\max(0,-u)\le x\le \min(n,n-u) \]可以画图理解一下。
区间长度为 \(\min(n,n-u)-\max(0,-u)+1=n+1-|u|\)。
因此合法的总个数为
\[\sum_{-n\le u\le n,u\equiv a\pmod w}(n+1-|u|) \]求法稍后介绍。
-
\(d\equiv1\pmod w\)
做法类似,此时代入原方程组得
\[x+y\equiv a\pmod w\\ x+y\equiv b\pmod w \]显然 \(a\equiv b\pmod p\),如果不成立一定没有解。
设 \(s=x+y\),我们要求 \(s\equiv a\pmod w\) 的数量。
这次,函数 \(y=s-x\) 必须在 \(0\le x,y\le n\) 的正方形中,所以 x 的取整范围为
\[\max(0,s-n)\le x\le \min(n,s) \]区间长度为 \(\min(s,2n-s)+1\)。
总答案
\[\sum_{0\le s\le 2n,s\equiv a\pmod w}(\min(s,2n-s)+1) \]
现在,我们要快速求
发现它们都是等差数列,可以用求和公式
快速求出。
对于第一个式子,可以拆分为
对于第二个式子,可以拆分为
我们可以写一个函数 sum(l,r,a,b) 计算
这样就适配了我们需要的所有求和,代码实现:
- 运用公式
first = L + ((a - L) % w + w) % w、last = first + ((R - first) / w) * w计算出首项和末项。 - 计算项数
cnt = (last - first) / w + 1。 - 套用求和公式求得 i 的和,
sum_s = (first + last) * cnt / 2。 - 乘上系数 a 和常数 b,返回
A * sum_s + B * cnt。
算这样一个求和是 \(O(1)\) 的。
最终的时间复杂度是 \(O(t\log w)\) 的,因为正常情况要计算逆元(100分)

浙公网安备 33010602011771号