2026.7.25 模拟赛
https://oj.gxyzh.com/d/hzoj/contest/6a63fd34ee9cc46434e73688
| T1 | T2 | T3 | T4 | |
|---|---|---|---|---|
| 实际得分 | 100 | 0 | 30 | 10 |
| 期望得分 | 100 | 0 | 30 | 10 |
| 能力得分 | 100 | 100 | 30 | 40 |
T1 是简单的乱搞题,T2 是树上背包,T3 是数论+数据结构,T4 是【?】.
修改进制
需要一些人类智慧以及丁真能力.
题目要求:
其中 \(e_i=0/1\).
拆开 \(b_i\),将 \(P\) 除过去有:
注意,这里有一条关键的信息:\(a_i\) 为互不相同的质数.
考虑将等式两侧化成最简分数的形式,那么右侧的分母为:
显然两侧的分母必然相同,将 \(d\) 与 \(P\) 进行约分,得到右侧的分母.
得到了 \(e_i\),直接验证即可..
这里验证有三种方式:
第一种是头铁的人的做法,第二种是正常人的做法,第三种是我的做法.
-
使用高精度模拟.
因为 \(n\le 10^3\),因此可以直接模拟,实现高精加/乘即可. -
使用哈希:具体地,选取几个大素数,计算等式两侧在模意义下的值,进行验证即可.
甚至你可以借鉴 Hash Killer III 的思想,选取随机模数,这样便无懈可击. -
使用
double类型直接模拟等式两侧.
表面上会炸精度,但实际上由于 \(a_i\le 10^9\),因此等式右侧数量级在 \(10^{-9}\) 到 \(10^{0}\) 之间,double完全可以进行计算.
对于等式左边,需要一些手法:可以通过乘一个,再除一个,这样能减少误差.
寻找小猫
场上有些降智,没有做出来(事实上本人对树上背包不太熟悉,截止 2026.7 还没有改省选 Day1 T1).
场后看这道题其实还是比较简单的.
由于发现花费时间与搜查叶子个数紧密相关,因此要在设计 DP 状态是加入对搜查叶子个数的记录.
手模样例一,可以发现,搜查小猫可以通过排除法,但是我们发现只能有一个子树使用排除法,因此在设计状态时加入可否使用排除法.
因此有 \(dp_{u,k,0/1}\) 表示在子树 \(u\) 中,选取 \(k\) 个叶子搜查,可否使用排除法,排除搜查叶子以外的最小代价.
于是有转移:
场上思考第二种转移,不知道如何实现,实际上就是一个二维背包.
最终答案需要加上搜查叶子的代价.
平方操作
最朴素的暴力可以拿到 20 分,特殊性质 A 可以拿到 10 分.
首先需要丁真 \(998244353\) 这个素数,考虑 \(a\) 模意义下的逆元,其实就是 \(a^{p-2}\).
于是我们可以得到:
因此只需要在最开始求一遍逆元,问题就转化成了区间平方,区间求和.
记操作次数为 \(k\),则我们要求的就是 \(a^{2^k}\) 在模 \(p\) 意义下的值,由于扩展欧拉定理:
因此考虑 \(2^k\bmod 998244352\).
场上考虑过 \(2^k\) 是否会造成循环,但是当时认为由于 \(2\) 与 \(998244352\) 不互质,没有进一步考虑.
我的OI数论物语果然有问题
如果你学过 NTT 的话,发现 \(998244353\) 这个模数真的是太熟悉了:
由于同余式的结论:
由于:
因此:
即当 \(k\ge 23\) 时,有:
但是 \(\varphi (119)=96\),事实上可以更小:
注意到:\(2^{24}\equiv 1\pmod {119}\)(\(24\) 是 \(2\) 在模 \(119\) 意义下的阶),因此:
总结一下:在 \(k\le 22\) 时,\(2^k\) 是独特的;在 \(k\ge 23\) 时,\(2^{k}\) 具有循环性质,循环节为 \(24\).
这时便能上数据结构维护了:
- 情况一,由于 \(k\le 22\),因此可以使用势能线段树维护.
由于这个势能线段树需要与情况二对接,因此这对于经常封装线段树的同学不太友好,
因此本人采用并查集+树状数组维护,理论常数更小. - 情况二,由于有 \(24\) 的循环节,因此可以将 \(24\) 种取值进行记录,修改时直接换成下一个值.
理论上的常数为 \(22+24\),但其实很大程度跑不满.
网格刷墙
这道题 \(O(nm)\) 是容易做到的,可以拿到 15 分.
不难想到后面的刷墙会覆盖前面的刷墙,因此只会有 \(O(n+m)\) 次有用的刷墙.
使用线段树维护一下,可以做到 \(O(n\log n)\) 将这些贡献离线下来.
(这里我们将最初的白色看作:横行在时刻 \(0\) 染色,竖列在时刻 \(-1\) 染色)
对于边相邻的情况,考虑分成先统计一行中边相邻的个数,一列同理.
按照时间添加顺序,从大到小考虑,我们将一行的贡献分成这几类:
- 横行染色的贡献.
- 竖列染色的贡献.
- 横行对竖列的贡献.
对于 1,2,我们在添加竖列的时候可以顺便维护,
对于 3,我们开一个桶记录每种颜色在边界的个数.
时间复杂度为 \(O(n\log n)\)(排序).
对于角相邻的情况.
记对于相邻的两行 \(a\) 与 \(b\),钦定 \(T_a<T_b\).
记对于相邻的两列 \(u\) 与 \(v\).
将贡献拆成 4 种:
-
\(a\) 的列染色,\(b\) 的列染色.
我们要求 \(T_a<T_u,T_b<T_v,C_u=C_v\).
那么按照时间从大到小考虑,对于一列,若两侧颜色相同,则将 \(T_u,T_v\) 加入数据结构.
在 \(T_a\) 时查询 \(<T_b\) 的贡献数量.
可以使用树状数组维护. -
\(a\) 的列染色,\(b\) 的行染色.
我们要求 \(T_a<T_u,T_v<T_b,C_u=C_b\).
按照时间从大到小考虑,对于一列 \(v\),将两侧的 \(v\) 的 \(T_v\) 加入 \(C_u\) 所在数据结构.
在 \(T_a\) 时查询颜色 \(C_b\) 中 \(<T_b\) 的贡献数量.
可以使用动态开点线段树维护. -
\(a\) 的行染色,\(b\) 的列染色.
我们要求 \(T_u<T_a,T_b<T_v,C_v=C_a\).
按照时间从小到大考虑,对于一列 \(v\),将两侧的 \(v\) 的 \(T_v\) 加入 \(C_v\) 所在数据结构.
在 \(T_a\) 时查询颜色 \(C_a\) 中 \(>T_b\) 的贡献数量.
可以使用动态开点线段树维护. -
\(a\) 的行染色,\(b\) 的行染色.
我们要求 \(T_u<T_a,T_v<T_b\) 并且有 \(C_a=C_b\).
这是经典的二维数点,可以使用扫描线统计.
综上,时间复杂度为 \(O(n\log n)\).

浙公网安备 33010602011771号