2025 暑假集训总结
Day 1
A
经典 trick。高位到低位考虑每一位能不能不取,不取的话就加到 limit 上。
B
构造题。考虑最终状态是什么样子的,一定是若干个区间相等。对于一个区间贡献上界就是 \(\max a_i - 0\),可以证明当 \(n > 3\) 时一定可以取到。此时发现整个序列作为一个区间去考虑就是最优的了。
G
二分答案。贪心从左到右扫,用一个优先队列维护 \(r\)。
F
容易发现如果一个矩形包含了头尾中的一个,那么返回的一定是奇数。先用 \(2000\) 次查询找到头和尾的行和列。如果行相同就二分找行,列同理。
C
卡 C 了。容易猜到答案一定是 \(0, 1, 2\) 思考怎么构造 \(2\) 的情况。赛时我 naive 的认为随便构造构造就出来了,然后就花了 3h 左右才把乱搞做法给过掉。正常的证明也是可以做的。先考虑 \(-1\) 的情况,一定是和为奇数或者存在一个数比剩下数都大。\(n = 2\) 只有两个数相等才可行。对于 \(n = 3\) 钦定 \(a_1 > a_3 > a_2\),令 \((a_1 - x) + (a_3 - x) > a_2\),一定存在 \(x\),合并前两次操作就变成了 \(2\) 次操作。对于 \(n\) 更一般的情况,直接分成三个块转换成 \(n = 3\) 的情况。
H
首先可以把一个串内部能匹配的就匹配了,剩下的就是前面一段 )
和后面一段 (
,然后自定义 cmp
排序。
E✨
神仙题,把奇数位 01
翻转,问题转换成每次消去相邻不同的位置。一个区间剩下的个数就是 \(|cnt_0 - cnt_1|\)。
trick: 将操作转等价换成更好处理的。
D
先考虑不相交的区间,一个选里面另一个选外面最优。包含的区间里面的选外面外面的选里面最优。剩下的就是相交的一坨区间,奇数里面偶数外面最优。
I✨
暴力应该都会。暴力时间复杂度之所以不对是因为每个状态的转移数量太多了,导致可能有不同的状态转移到相同的状态。只要能够把这个状态图建成一颗树,那么转移的边数就变成了 \(k\)。题目变成了构造优秀的转移 \(trans\) 不重不漏的转移。这种构造题一般都不是人想的。结论就是:考虑当前一定有一个考虑到的位置 \(pos\),还有这个位置选的的深度 \(dep\),则有 \(3\) 种转移:
- \(dep \leftarrow dep + 1\)
- \(pos \leftarrow pos + 1, dep \leftarrow dep + 1\)
- 若 \(dep = 1\),则 \(dep \leftarrow 0, pos \leftarrow pos + 1, dep \leftarrow dep + 1\)
发现通过这 \(3\) 种转移,相邻的都加一和不相邻的都加一都有且只有一种方案。
因为需要保证每次转移都更劣,因此需要按照 \(a_{i, 2} - a_{i, 1}\) 从小到大排序。
trick: 求前 \(k\) 个的最优化经典模型。
J✨
神仙题。考虑把每个状态抽象成点,每次操作抽象成点与点之间的边,建出图来发现是一颗树。中间的点往两边跳理解成二叉树上往儿子跳的边,两边的点只能有一个往中间跳理解成跳父亲。发现当两边的点距离相等时就无法跳父亲了,此时就到了树的根节点。因此可以把初始状态和结束状态所在的根节点求出来,如果不在同一棵树内,则无法跳到。
接下来考虑怎么从初始节点跳到结束节点。直接跳不知道方向,因此可以仿照树上求 lca,找到把它们的 lca 当作中间点,初始节点和结束节点都往中间点跳。如果求出两个状态的 \(dep\) 再去一个一个跳时间复杂度是错的。考虑加速跳的过程,如果同一边能一直跳就计算好跳的次数。显然的,设两边的长度为 \(d1,d2\),则一轮过后,\(\max(d1,d2) \leftarrow |d1 - d2|\),最少也会除以 \(2\)。
trick: 将问题抽象建模。显然如果这道题不建模除非你有高斯的注意力根本不可做。
杂题 1
杂题 2
Day 2
模拟赛
T1
100pts / 10min。
单调栈记录每一个位置 \(nxt\)。定这个位置为 \(l\),则 \(r\) 一定是被每一个 \(nxt\) 分成一段一段的贡献,容易发现贡献就是 \(\sum{n - p + 1}\)。
T2
20pts / 2.5h
挂惨了,打个暴力都有 70 分。容易发现一个区间能被碰瓷需要满足以下几个条件:
- \(b\) 是区间的子序列
- 第一个数相等
- 不能复制相等的数
第三个条件赛时推了但是推错了,以为是 \(b\) 的下一个 \(a\) 不能和它相等,思路直接假了,然后最后 30min 准备对拍但是暴力不太好写就不想对拍了。所以以后题目要想清楚再去写。
实际上第三个限制讲的是 \(a\) 前面一段相等的长度要小于等于 \(b\) 的,这样后面只要匹配到最前面的子序列就一定可以构造出合法的方案。
只能说 T2 挂了确实不应该。没对拍是最大的错误。
T3
70pts / 10min
怎么有人模拟赛直接扔板题啊。70 分直接跳 border 就可以了,证明左转 oi-wiki。
发现每一个周期能否成为周期是有单调性的,一旦不是就再也不是了,因此可以二分。
赛时还想了一种不知道到正确性的做法。记录每个点的 nxt,如果当前往反方向插跟正着插是等价的,因为一个 border 翻转过来一定还是 border,只有比较的字符要换一下。直接跳 border 显然不太行,可以记忆化一下。
T4
2pts / 50min
赛时注意到一个合法的子序列一定满足:前缀和后第一个最小最后一个最大。T2 占据太多时间导致有点急了。推到这里只会 Dfs。
赛后发现最优的一定是 \(1\) 能选就选,\(-1\) 只要不会让前缀降到 \(-1\) 能选就选。最终会导致 \(s_n\) 不一定是最大,这时候从后往前删掉一些 \(-1\) 就可以了。
听讲解发现 \(mx\) 就是最大子段和,经过证明可得只需要删去 \(mx - s\) 个 \(-1\) 就一定可以让这个子序列合法,因此答案就是 \(r - l + 1 + mx - s\),线段维护。
总结
总体来说 T2 是个失误,有了 T2 的时间可以把 T3 的剩下 30pts 给打了,T4 至少有 30pts。
每道题都要对拍!!!
习题课
A
注意到最大值一定会第一次就被选,因此 \(n ^ 2\) 做完了。
F
考虑把 \(\operatorname{get}(l, r, x)\) 拆成 \(\operatorname{get}(0, r, x) - \operatorname{get}(0, l - 1, x)\),离线之后莫队维护。
E
考虑从小到大排序,零当前选择的数最大是 \(mx\),可以表示 \([0, pos]\) 的数,则只有值域在 \([mx + 1, pos + 1]\) 内的值对答案才有贡献,记它为 \(sum\),则 \(mx \leftarrow pos + 1\), \(pos \leftarrow mx + pos\)。主席树维护 \(sum\)。
D
因为 \(b\) 是一个排列,注意到每一个值变动的次数总和为 \(n \log (1e9)\),暴力重构树就可以啦。
B
断环成 \(3\) 个链,当整个数列最小值依然满足最大值的条件时,就会无限循环。\(i\) 从后往前扫,记 \(pos_1\) 表示 \(i\) 后面第一个满足 \(a_{pos_1} > a_i\) 的位置,\(pos_2\) 表示 \(i\) 后面第一个满足 \(a_{pos_2} < \frac{a_i}{2}\) 的位置。如果 \(pos_1 < pos_2\),则 \(ans_i = ans_{pos_1} + pos_1 - i\),否则等于 \(pos_2 - i\)。
C
多打几个 tag 就结束了。
Day 3
A
从 0 下手。容易发现最后一个 0 就是 1 所在的位置,把 \(1\) 后面的值全部减去 1,那么现在的最后一个 0 就是 2……。依次类推,直到找到 \(n\)。
E
因为要让 \(A_i + A_j - \min{B_k}\) 最大,所以里面的 \(\min\) 实际上是没用的,接下来线段树就可以只需要维护 mxans, mxA, mnB, mxlA-rB, mxrA-lB
就做完啦。
B
直接颜色段均摊。
G
考虑问题的弱化版:求一个区间内有没有相等的两个数。比较显然的,记录每一个元素前面的最靠后的和它相等的位置 \(pos\),则问题转化成判断 \(\max_{i=l}^r{pos_i} \ge l\)。修改只需要修改这一个位置,原本的后面的位置,改之后后面的位置。
回到这个问题,\(pos\) 变成最后以个等于 \(w - a_i\) 的位置。但是修改就需要修改很多个值。仿照弱化的问题,思考为什么弱化版不需要改很多值,因为再后面的和它相等的值改它就没意义了。回到这道题,类似的,如果 \(pos_i\) 小于上一个和它相等的位置,则改它毫无意义,因此每次改只需要改第一个位置。总共要改 \(5\) 个位置。
D
维护每个有值的下一个位置连续空的长度,线段树上二分。
H
线段树分治板子,需要并查集打 tag
。
C
讲一下自己想的抽象做法。
容易发现操作的区间即使有相交也可以用不相交的区间来表示出来,因此最终的状态一定是一段段操作过的区间。
令 \(f_i\) 表示考虑了前 \(i\) 位的答案,\(g_i\) 表示钦定 \(f_i\) 中钦定第 \(i\) 位不被删去的方案数,\(j\) 表示 \(i\) 前面最右边的 \(a_j < a_i\) 的位置,没有则为 \(0\)。显然 \(f_i\) 先加上 \(f_{i - 1}\)。\(i\) 的影响就是删去一个后缀,但是如果删过了 \(j\) 就会被 \(j\) 给删掉,也可以理解成把 \([j, i]\) 看成一个块,因此 \(f_i\) 再加上 \(f_j\)。对于中间的 \(k \in [j + 1, i - 1]\),如果 \(a_k\) 不是考虑到第 \(i\) 位的后缀最小值,也就是 \(k\) 本身不能把自己的后面全删掉,那么就可以用 \(i\) 删掉,答案加上 \(g_k\)。发现 \(g_i = f_i - f_j\)。\(j\) 和 \(k\) 这一维可以用单调栈优化掉。
I
排序之后问题转化成如果与 \(x \ge c_i\),则减去 \(c_i\)。
考虑把询问离线,则会分成两个部分。\(val \ge c_i\) 的部分答案加 \(1\),\(val \leftarrow val - c_i\)。\(val \in [c_i, 2 \times c_i)\) 的部分相对位置会发生变化,\(val \in [2 \times c_i, ∞)\) 的部分相对位置不变,可以打 \(tag\)。
显然在中间的部分的值每次至少减少一半,因此每个数最多只有 \(\log\) 次操作,平衡树暴力维护就行。
此外,还有对值域二进制分解的做法和另一种做法。
考虑倒着做。令 \(f_{i, j}\) 表示后 \(i\) 个物品初始时是 \(j\) 能拿几个。转移有:
容易发现操作就是复制和拼接,可以使用可持久化平衡树自拼接的技巧做到单 \(\log\)。
J
Day 5
模拟赛
T1
100pts / 10min
记一下前缀、后缀的最小、最大子段和。
T2
100pts / 30min
枚举 \(h_{max}\) 的玩偶,将大于 \(h_{max}\) 的全删了,剩下的删最小的。平衡树、线段树都可以维护。
T3
100pts / 120min
最朴素的想法就是不够了就上升,但是不知道什么时候下降。考虑把上升的高度在一开始就上升了,则一定是先上升到一段高度,再平着飞,最后下降。但是这样依然不好维护,思考平着飞是不是必要的。显然不是,可以先上升到最高点,再下降。但是如果中间的距离不是奇数,则还需要平着飞一段。
这时候,往上和往下的过程完全是对称的,便可以钦定一个点为最高点,从开头和结尾分别跑一遍最短路,则它的代价就是 \(\max(d1, d2) \times 2\),因为这时候它的 \(dis\) 就是它的高度,如果一边的高度小了则需要在开始的时候加高度。此外,还需要考虑两个相邻的点,但是代价需要加上中间平飞的 \(1\)。
这道题还有另一种更轻松的做题方法。令 \(f_i\) 表示第 \(i\) 个点最少花费的时间,则 \(f_{i + 1} = \min{\max(a_i + 1, f_i + 1)}\)。推出这个式子就可以发现上面的所有性质,并且 \(f\) 可以用 Dij 来求。
T4
30pts / 80min
前 30min 在打 subtest。自信猜测选的区间一定是一段,然后大样例就挂了。
赛后发现如果按照 \(r\) 排序,则相互包含的区间会影响答案。因此需要把相互包含的区间给搞掉。经过证明,如果一个大区间包含了一个小区间,则大区间要么单独分组,要么和小区间一组。因此便可以单独分出来不包含的区间来 DP。DP 有人说可以 wqs 二分优化,但是并没有凸性。比较显然的,因为是求区间最值,\(l\) 单调不降,可以把 \(k\) 这一维提出来,单调队列优化。
总结
330pts 撞大运了。
T3 的做题过程漏洞太多了,首先正常人估计 3min 就能推出来先上升后平飞最后下降,我推了 30min。其次就是推出没必要平飞和两遍 Dij 又花了 30min。再最后就是我贡献算成了 \(d1 + d2\),差点放弃去做下一题了,是最后自信猜测是 \(\max(d1, d2) \times 2\) 才过的。题做少了,做题的思路没找到。再就是不够自信,我一开始就猜测贡献可能是 \(\max(d1, d2) \times 2\),但是一开始大样例没过就没试。
T4 有点盲目自信了。赛时竟然没有找到直接做错在哪里。把包含的去掉我记得是一个比较经典的 trick,但是没有考虑到。只要想到这个 trick,这道题就做出来了。
还是太菜了。各个方面都存在漏洞。最后就是实现的能力还是太差了。别人乱搞 T4 都有 50 多分甚至卡到了 100pts,一个同学 T3 性质没推完直接写 Dij 也过了,而且只用了 60min。
习题课
A
从后往前扫,拿个东西维护每个值的出现次数,树状数组维护答案。
B✨
好题。
这类题目有两种做法:
- 枚举一个端点,通过单调栈来维护另一个端点的信息。
- 笛卡尔树分治,枚举最值,枚举小的一边。
先说我的做法。从后往前枚举最值,单调栈限制 \(l, r\) 的范围,再开一个单调栈维护后缀的最小值,同时预处理出每一个数的前面第一个比它小的位置 \(lst\),则 \(r\) 需要满足第二个单调栈中最后一个小于等于它的位置的 \(lst \ge l\)。先二分找到这个位置,然后可以通过数据结构对从栈顶到这个位置的 \(lst\) 求和,减去对应个 \(l_{min}\) 就是这个位置的答案。
再说一种笛卡尔树分治做法:类似我上面的做法,只不过这次二分变成枚举,更容易写。
还有一种枚举端点的优美做法。对于每个 \(r\) 如果 \([l, r]\) 是合法的,则令 \(t_l = 1\)。注意到只有 \(a_r\) 为最值时候 \(t\) 才会发生变化。令 \(i\) 前面第一个大于 \(a_i\) 的位置为 \(b\),则对与所有 \(l \in [b + 1, i]\),\(a_i\) 是最大值,\(a_{i-1}\) 是最小值,所有区间都满足,区间赋 \(1\)。第一个小于 \(a_i\) 的位置同理,区间赋 \(0\)。可以不写线段树,因为赋值的左端点均摊 \(O(n)\),可以维护 \(01\) 段。
C
因为 \(x\) 单调递增,因此每个数只会转变一次,只需要记录:
int s1, c1, mn, s2, c2;
int tag1, tag2_add, tag2_times;
// 1: >, 2: <
然后暴力 rebuild()
就行了。
D
先离线下来把位置当成轴来扫。需要一个数据结构维护后缀加,前缀查询大于某个数的数的个数。规约问题发现只能分块做,每个散块加完排序,大块打 \(tag\)。查询时二分。
E
F
显然,题目要求有多少个子段是连续的。考虑对 \(r\) 扫描线,则合法的 \(l\) 需满足:\(\max - \min + l - r = 0\)。
也就是说需要区间加操作。\(\min,\max\) 可以通过单调栈均摊维护。
我记得很久之前我还口胡过一种 cdq 做法,就是把左右两边分成四种情况去考虑。比较复杂但是可以做。
这道题还有升级版:CF997E Good Subsegments。把全局查变成了 \(q\) 次区间查。
考虑对 \(r\) 的询问扫描线,则问题转换成了类似矩形求和,对于一列的一部分进行修改。矩形求和可以拆成 \(4\) 个前缀求和。但是每次我们只好维护出 \(\Delta\)。因此考虑对于每一个单点维护出 \(S_{\Delta}, Sum\),每一个区间维护出 \(S_{S_\Delta}, S_{Sum}\)。每次区间改就好维护了。
其实这就是比较简单的历史版本线段树。当然这道题还有科技(析合树) 。
Day 6
A
可以把每次操作看成循环右移,DP 发现可以把下标分成两类:1 和大于 \(1\) 的。朴素 DP 即可。
B
维护一个栈去跑 KMP,但是这种操作虽然带有删除,但是因为栈满了会弹依然可以势能分析出是均摊的。
C
这道题就不能头铁直接 KMP。\(10 ^ 6\) 个 \(\texttt{a}\) 直接爆炸。因此考虑 KMP 自动机。
D
base = 10
的哈希。
E
比较复杂的题,贪心简单不好证明。首先就是头尾能选就选,只要不重叠。因此考虑跑一遍 Manacher。
F ✨
好题。广义 KMP 可行当且仅当等于满足传递性,且 \(S = T\) 当且仅当对于所有的 \(i\),都满足 \(S_{1 \dots i} = T_{1 \dots i}\)。先把 rank->id
转换成 id->rank
。KMP 本质相当于 \(r\) 不变,\(l\) 一直跳,因此跟着 \(l\) 维护一个树状数组求排名是均摊的。
还有一种不需要带 \(\log\) 的做法。考虑在 \(p\) 中求出每一个位置的 lstpos, nxtpos
,则新加的位置合法仅当将它在 \(p\) 上的前驱后继对应到 \(h\) 上依然满足这个关系。前驱后继离线可以用链表 \(\operatorname{O}(n)\) 求。
H
SA 板题。考虑把原串翻转一下。则限制变成了若干个开头是锁定的。后缀排序之后,问题转换成 \(h\) 的最大矩形和。对于锁定的开头,就把它与前后的合并(取 \(\min\))。
I
AC 自动机板题。考虑拆贡献。问题变成对于原串的每一个位置 \(i\),求出有多少个匹配串以它开头和结尾。
此外还可以根号分治,具体看官方题解。
Day 7
模拟赛
T1
100pts / 40min
比较显然的,\(f(x, y) \le 2\),当且仅当 \(x \operatorname{and} y = 0\) 时,\(f(x, y) = 1\)。因此直接容斥做完。
T2
100pts / 80min
「能把这道题扔进模拟赛的家里得请高人了👍👍👍」
比较显然的,有两个以上的不合法段一定不合法。考虑有两段,则一定选中间的位置最优。一段的话枚举第一个选的位置,往左选还是往右选,即当前两边的连续长度为 \((lenA, lenB)\),则选的 \(pos\) 两边的长度为 \((lena, lenb)\),若颜色不同则为 \(-\inf\)。需满足 \(max(lana + lanA, lenb + lenB) < k\),排个序记录一下前缀最小值。
好题,极大的锻炼了我的实现能力,但是 176 行代码一遍过还是挺开心的。但是别的同学只用了 76 行……
T3
100pts / 60min
一眼二分。前面在想怎么贪心,但是 \(k \le 17\) 启发我状压。直接状压时间复杂度直接爆炸,考虑把状态 \(S\) 作为状态,选的位置 \(pos\) 作为附带属性,取最小值一定最优。到这里已经有拓扑序了,可以直接 DP。但是我赛时还套了个 Dij 去跑,出题人没卡我这 \(log\)。
T4
0pts / 60min
差一个 push_up
就写完了。
考虑把每一个位置抽象成一个操作。那么对于线段树上的一个节点,可以存储一个函数(也可以理解成映射),输入进入节点的时间,返回离开节点的时间。比较显然的,这个函数可以分成 \(3\) 段:
这个函数显然是可以合并的,并且合并之后依然满足上面的形式。
发现每个操作都可以通过这个函数来表示,于是便可以线段树单调修改区间查询维护。
但是这种做法的 push_up
完全不是人写的,各种边界爆炸大分讨,简直就是达芬奇没有奇。赛时 40min 有 30min 都是在调 push_up
,比赛结束了都没调出来。
这道题也是一道 DDP 板题,维护 \((\min, +)\) 矩阵就结束了。赛时过了的有一大半都是矩阵哥。
还有更容易理解的做法:把两个点合并成一个点,每个点维护三个值:允许通过的最晚时间,最快什么时候到下一个点,到下一个点需要的时间。
合并的话有:
N.lstArTime = max(l.lstArTime, r.lstArTime - l.dis);
N.nxtArTime = max(l.nxtArTime + r.dis, r.nxtArTime);
N.dis = l.dis + r.dis;
只需要满足 l.nxtArTime <= r.lstArTime
。
总结
写代码要写容易实现的,不要复杂化问题。
Day 9
Day 10
模拟赛
T1
100pts / 10min
钦定最小,维护后缀 \(\max sum\),如果都大于最小值,则降级代价最小的。
T2
100pts / 120min
没睡好,状态不行,这么简单的题卡了我 2h /(ㄒoㄒ)/~~。只需要意识到按位与得到的结果与原先的数是包含关系,就可以直接递推去做。不过要注意四种情况都要统计到,很多人因为这个挂分了。
T3
0pts / 60min
觉得这道题带点 ad-hoc 和构造性质,重心就没有放在这上面。
T4
9pts / 60min
赛时想到了如果一个数是接下来第一个被选的,则它的值减去后面的选了的值的和是最小的,且它的位置最靠前,只打了 \(n ^ 3\),\(\text{subtest2}\) 最后一个点还被卡常了。
总结
晚上早点睡,想题不要死磕,遇到明显不可做的方法就换一种视角。
Day 11
A
模拟二分查找的过程,则会有若干个位置满足大于 \(x\) 或者小于 \(x\) 的限制,组合数学。
B
显然的,把最大的放在最前面一定最优。
当存在 \((n, n)\) 时,答案为 \(1\),方案数就是剩下的全排列。不存在时,把 \(first = n\) 的移到最前面,记它的 \(second\) 为 \(x\)。另一个 \(second = n\) 的需要满足它到 \(x\) 中不存在大于 \(x\) 的值,可以组合数,最小值为 \(3\)。将 \(second = n\) 的移到前面同理。
C
打表题,略。
D
我一开始真的写的是头铁判断 \(a + b > c \dots\),然后优化不了才换思路。并不显然的是正着做需要三个限制,但是反着容斥只需要满足一个限制。因此考虑容斥去做。先考虑 \(a > b + c\),枚举 \(a\) 加上 \(i\),则 \(b, c\) 至多加上 \(l - i\),且需要满足这个限制,对答案的贡献就是从 \(1\) 开始求和。另外两种情况同理。
E
先 \(n^3\) DP 起手发现优化不了。因为这是一个平衡树,很显然的是,只需要中序遍历就可以把它等价转化成一个单调不降的序列。现在填了若干个数,中间空的部分可以插板法,但是也可以暴力推式子。记分了空的长度是 \(len\),可填的值域范围是 \(len2\),则总方案数为:
范德蒙德卷积后变为:
与插板法式子相同。
F
首先,把每个位置按照前面与后面一个是否相同分组。如果相同则这个位置可以随便填。对于不相同的,需要满足选后面的个数严格大于选前面的个数。一个经典 Trick 就是前面大于后面和后面大于前面实际上是对称的。因此只需要把相等的情况减去,剩下的情况除以 \(2\) 就是最终答案。
G
\(n\) 个节点的本质不同二叉树个数就是 \(H_n\)(\(H\) 是卡特兰数)。先把点数小于 \(n\) 的全部加上。接下来考虑分治去做。分治去左子树,则右子树随便选,系数带上 \(H_{sz_{rs_u}}\)。分治去右子树,则左子树必定与这棵树的左子树同构,系数为 \(1\)。剩下的枚举左边选 \(i\) 个,右边的也确定了,对答案的贡献为 \(\sum_{i = 0} ^{sz_u - 1} H_i \cdot H_{sz_u - 1 - i}\) 还要带上系数。
\(n ^ 2\) 过不了,只需要把求和这一部分启发式合并了。如果右子树小就容斥去算。
H✨
先把用过的行和列都删了。直接做接下来选的横着的和竖着的会互相影响,因此只能状压做……吗?并非。先考虑只选竖着的情况,则答案就是 \(\operatorname{A}^{cnt_w}_{i} \cdot f_{cnt_h,i}\),\(f\) 表示相邻列空着的选的方案数。先不考虑别的,加上 \(j\),则答案就是 \(\operatorname{A}^{cnt_w - 2j}_{i} \cdot f_{cnt_h,i} \cdot \operatorname{A}^{cnt_h - 2i}_{j} \cdot g_{cnt_w,j}\)。因为横着的会占两列,竖着的会占两行,但是会影响 DP 的值吗。并不会,因为只要他们两个都满足了前面的 \(\operatorname{A}_{...}^{...}\)(这两个限制会相互制约),那么它们就一定不会在同一行或者同一列,因此不会影响 DP 的值。
Day 13
模拟赛
T1
100pts / 2h
不是哥们,赛时就几个人写的正解,其他的都是暴力卡过去的。
一开始想到二分,但是类似之前的某道题目,这道题并没有单调性。每次 check
可以用 multiset
做到 \(\operatorname{O}(n)\)。观察到二分出的答案一定是只大不小,并且与正确答案相差不多。因此考虑在 \([l - 1000, l]\) 内枚举答案检验。
接下来 1.5h 都在想怎么通过数据结构维护值,然后就卡死了。
正解好像是容量的下限是 \(\frac{\sum v_i}{k}\),上届是下届加上 \(\max{v_i}\),证明略。
T2
15pts / 2h
T1 心态爆炸后看 T2。一开始以为是讲过的一个 Day1 E 的 Trick。冲了 30min 后发现它不能合并。仔细思考后发现是贪心模拟题,考虑把每个位置分开来做,但是太复杂比赛结束后都没有调出来。
实际上可以把每个 \(\texttt{?}\) 段一起考虑。先把所有 \(\texttt{?}\) 填 \(\texttt{0}\),对于 \(\texttt{00}\) 段它的贡献是 \(+1\) 后变成 \(\texttt{01}\) 段,\(\texttt{01}\) 段的贡献始终为 \(0\)。\(\texttt{11}\) 段贡献是 \(0\) 后减 \(1\)。
显然先选 \(\texttt{11}\) 段,按照长度从小到大排序,接下来是 \(\texttt{01}\) 段,最后是 \(\texttt{00}\) 段,长度从大到小排序。
T3
0pts / 0h
01trie 上线段树分治。
T4
0pts / 0h
分类讨论,贪心。
总结
最惨的一次。模拟赛基本上第一道题心态爆炸那么整个模拟赛也炸了。要控制好时间,冲题不要上头了,基本上 1h 冲完冲不动了就要考虑拿部分分了。还有就是心态,一道题爆炸就去看下一道题,不要上头不要上头不要上头。
习题课
A
签到题,略。
B
容斥,总方案数减去按照 first
排序的方案数减去按照 second
排序的方案数加上两个都排的方案数。
C
拆贡献。考虑分析每一位对答案的贡献,排序之后把等于的情况考虑进去推一下式子就做完了。
D
E
F
Day 14
A
发现开头结尾的元素只有 \(1\) 个,第二个元素只会出现 \(2\) 次,直接模拟即可。
B
有点意思的题目。正着走不知道封锁哪边最劣,考虑倒着跑一遍。注意到封锁 \(d_i\) 等同于舍弃 \(d_i\) 次到这个点的最短路,因此直接跑一遍 Dij 就行了。
C
Floyed + DP。
D
因为拥堵系数只由两条边来决定,因此考虑枚举一条中间的边(防止左右两边重合),分别跑一遍从开头开始的和从结尾开始的最短路。
E
因为最短路唯一,建出最短路树之后思考每一个非树边能对答案产生什么贡献。假设有非树边 \((x, y, w)\),则对于点 \(u\) 的贡献为 \(dis_u - dis_x + dis_y + w\),并且 \(u\) 得在 \(x, y\) 各自到 \(lca\) 的路径上,不包括 \(lca\)。 把每条边的贡献排序之后树剖维护区间赋值就行了。
H
二分图最大匹配。
Day 21
Day 23
T1
100pts / 30min
模拟题,区间异或离线差分即可。
T2
0pts / 1.5h
#define int long long
怎么你了😡😡😡。
前一个小时都在找规律,找操作策略,没什么收获。这道题区间改区间查猜也能猜到线段树维护某个值,并且两段区间是否相等和两个区间无关。想了合并 \(\texttt{01}\) 段但是没有结果。手玩若干年随机数据后发现通过两次操作, \(\texttt{1}\)在连续 \(0\) 段可以通过两次操作移到 \(pos - 2\) 和 \(pos + 2\),也就是说,任何奇偶位置的 \(\texttt{1}\) 可以合并消掉,手玩样例发现是对的。15min 写完结束。
你说得对,但是 \(4 \times 10 ^ 6\) 个 long long
被卡空间了,100pts \(\rightarrow\) 0pts。
有另一种秒题方法。考虑 Day1 E 的 Trick。则每次可以把奇偶不同的 \(\texttt{01}\) 消掉,直接出结论。
T3
10pts / 30min
一开始没用把它从环上去考虑,直接从链上做不好做。
考虑把链拆开两个长度为 \(n\) 的部分,两条链匹配,类似最长公共子序列,我们把两条链分别称为 \(a\) 序列、\(b\) 序列,令 \(f_{i, j}\) 为 \(a\) 序列前 \(i\) 个与 \(b\) 序列前 \(j\) 个能得到的最大点数。
-
为了让 \(a_i\) 与 \(b_j\) 匹配,应在 \(1 \sim i - 1\) 找到 \(a_k = b_j\),在 \(1 \sim j - 1\) 找到 \(b_l = a_i\),使之满足两两匹配。
-
不匹配,则 \(f_{i, j} = \max(f_{i - 1, j}, f_{i, j - 1})\) 。
找 \(k, l\) 可以预处理,因此优化成 \(\operatorname{O}(n ^ 3)\)。
赛时 DP 没太想清楚,没做出来实在不应该。
T4
30pts / 1.5h
双 \(\log\) 做法怎么你了😡😡😡。
T4 一点思维难度都没有。树上 \(dis\) 肯定就是点分治。考虑按位拆贡献统计答案。对于第 \(i\) 位为 \(x\) 的数来说,只有另一个数 \(y\),满足 \((x + y) \bmod 10 = 5\) 且进位,\((x + y) \bmod 10 = 6\) 且不进位才有贡献。因此考虑对于每一位每一个数都开一个数据结构维护加数和求 \(rank\),总共是 \(63\) 个。
因为比较卡空间,我就开了 \(63\) 个 Treap。然后结束前 \(5\) 分钟极限写完结果 \(3132\text{ms}\) 被卡成暴力分了。
没看时限确实是我的问题。可以考虑通过基数排序和双指针把 \(\log\) 去掉,跑得飞快,也不难写。
总结
T2 被卡空间,T4 被卡时间,挂了 160pts。不过 T2 但凡我自测一下都可以避免 MLE,是我的问题。T4 没看时限也是我的问题。但是 T3 没做出来实在是有点抽象,多做点 DP 题吧 >﹏<。