做题笔记五
发现打 duel 的时候做题效率大幅提升,怎么回事呢。
这里还包含少量 duel 以外的做题笔记。
My Acc:\(\color{aa00aa}\textsf{tril0713}\)。
打 \(^*\) 的是看题解才会做的,当然如果你在 duel 的题目里看到打 \(^*\) 指的是 duel 结束才看的题解。
- CF1017D \(\color{aa00aa}1900\)
\(\color{aa00aa}\textsf{tril0713}\) 胜 \(\color{aa00aa}\textsf{lldxjw}\)(\(\color{aa00aa}2060+56=\color{ff8c00}2116\))
本质不同的 \(01\) 串只有 \(2^n\) 种,直接预处理出两两距离,查询二分即可。\(\mathcal O((4^n+q) \log n)\)。
- CF1080F \(\color{ff0000}2400\)
\(\color{ff8c00}\textsf{tril0713}\) 胜 \(\color{0000ff}\textsf{liangjiaqi}\)(\(\color{ff8c00}2116+9=\color{ff8c00}2125\))
唐氏 DS 题。
考虑扫描线扫描右端点,询问相当于查询 \([l,r]\) 颜色中每种颜色当前最右侧的 \(l\) 的最小值是多少。
强制在线直接上主席树即可。
写代码的时候犯唐了,可持久化的时候继承错状态了调了 10min,但是对手直接认输了,导致我还是赢了(x
- CF380B \(\color{ff0000}2400\)
\(\color{ff8c00}\textsf{tril0713}\) 胜 \(\color{808080}\textsf{yaohaoyou}\)(\(\color{ff8c00}2125+1=\color{ff8c00}2126\))
子树在每一行上都是一个区间,注意到颜色数量很少,直接枚举每种颜色的所有出现位置判断是否和子树对应行区间有交即可。复杂度 \(\mathcal O(nm)\)。
唐诗出题人给的伪代码会 MLE 掉,必须快速计算一个点的左右儿子编号。我由于数组开小报 WA,MLE 后卡常浪费大量时间,但是最后还是惊险拿下胜利。
- CF1709F \(\color{ff0000}2500\)
\(\color{ff8c00}\textsf{tril0713}\) 负 \(\color{ff0000}\textsf{Graygoo\_401}\)(\(\color{ff8c00}2126-11=\color{ff8c00}2115\))
其实不是很难的题,但是在读题和一些转移细节上浪费了太久。
用 01 trie 刻画字符串的前缀结构,设 \(f_{u,k}\) 表示 \(u\) 子树内最多可以选出 \(k\) 个串的填 \(c\) 方案数。
首先合并两个子树(这里是直接卷积),得到两棵子树恰好选出 \(k\) 个的方案数 \(f'_{u,k}\),然后考察新节点的填 \(c\):填 \(c\) 会导致 \(\forall k \in [1,c),f'_{u,k} \to f_{u,k}\) 和 \(\forall k \ge c,f'_{u,k} \to f_{u,c}\)。前缀和一下即可。
复杂度 \(\mathcal O(nm \log m)\)。
- CF476D \(\color{aa00aa}1900\)
\(\color{ff8c00}\textsf{tril0713}\) 负 \(\color{0000ff}\textsf{liangjiaqi}\)(\(\color{ff8c00}2115-110=\color{aa00aa}2005\))
离谱,这题竟然花了我 30min,然而对手 5min 秒了。
首先 \(k\) 是来搞笑的。一开始我一直在把互质往质数方向推,直到交了一发 wa 掉了。一个 pair 里三个质数不是最优的。
考虑尽可能选小的数字,不难发现一个元组里只能有 1 个偶数。考虑每个元组里都取 3 个相邻奇数,这一部分显然达到最优;然后选择一个尽可能小的和它们都互质的偶数。如何证明这样最优?
-
对于小数据可以暴力打表验证;
-
对于较大的 \(n\),由于偶数还剩很多没有使用,很容易找到和三个奇数都互质的偶数,不会造成额外开销。
-
CF121D \(\color{ff0000}2400\)
\(\color{aa00aa}\textsf{tril0713}\) 胜 \(\color{0000ff}\textsf{jjh0030}\)(\(\color{aa00aa}2005+54=\color{aa00aa}2059\))
差点又输了,哈哈。
首先需要注意到,值域内的幸运数字其实只有大概 \(5 \times 10^5\) 个,这使我们可以枚举一个端点。
二分答案 \(mid\),枚举左端点 \(l\) 即可得到右端点 \(r\)。显然不能存在长度短于 \(r-l+1\) 的区间,因此移动后 \([l,r]\) 不会包含任何区间。考虑将其它区间移动过来,对于 \(l'>l\) 的区间需要移动 \(l'-l\),对于 \(r'<r\) 的区间需要移动 \(r-r'\),双指针即可。int128。
- CF1776G \(\color{ff8c00}2100\)
\(\color{aa00aa}\textsf{tril0713}\) 胜 \(\color{0000ff}\textsf{bhbjzyh}\)(\(\color{aa00aa}2059+27=\color{aa00aa}2086\))
猜了一堆东西,最后终于是猜出来了。
结论是,选择长度为 \(n\) 的最大子段和 \([k,k+n-1]\) 一定合法。
考虑 \(l < k\) 的位置 \(l\),其第一个和等于 \(x\) 的区间右端点 \(\le r\) 且长度至少是 \(n\),\(r > k + n- 1\) 同理。这些区间就已经有 \(n\) 个。
- CF87D \(\color{ff8c00}2300\)
\(\color{aa00aa}\textsf{tril0713}\) 胜 \(\color{008000}\textsf{Gheazedrokzoe}\)(\(\color{aa00aa}2086+3=\color{aa00aa}2089\))
考虑如果 \(w_i\) 互不相同怎么做:此时可以直接按 \(w\) 从小到大合并连通块,一条边的权值即为其连接两端大小的乘积。
如果存在相同的,那么把这些连通块建树后统计每条边两端的 siz 乘积即可。
题解给出了更智慧的办法:最开始建 dfs 树,处理相同边权时按照深度合并,这样当前的 siz 就直接是子树大小。
- CF232B \(\color{aa00aa}1900\)
\(\color{aa00aa}\textsf{tril0713}\) 胜 \(\color{0000ff}\textsf{Physics212303}\)(\(\color{aa00aa}2089+24=\color{ff8c00}2113\))
注意到第 \(i\) 列选的点数和第 \(i+n\) 列相同,因此只需要 dp 前 \(i\) 列的选择点数并乘上组合数的次幂作为系数即可。
直接暴力跑背包 dp 就是 \(\mathcal O(n^4)\) 的,可过。
- CF993E \(\color{ff8c00}{2300}\)
我们只关心数字和 \(x\) 的大小关系,问题变成了 01 序列,对每个 \(s\) 求有多少子区间的和等于 \(s\)。
分治后变成卷积问题,复杂度 \(\mathcal O(n \log^2n)\)。本题前缀和单调不降,可以运用差卷积技巧做到单 \(\log\)。
- \(^*\)CF2026F \(3\color{ff0000}200\)
在线很难搞,考虑建出操作版本树然后离线 dfs。问题可以抽象成求链上背包的答案,点分治可以做到 \(\mathcal O(nv \log n)\),显然不优(虽然能过,早知道能过就赛时写了 /fn)。
我们的原问题其实远远简单于「链上背包答案」,从父亲走到儿子其实只是 push back 和 pop front,回溯则是做相反的操作:pop back 和 push front。
如果只有前两种操作,那就是经典的 baka's trick:双栈完成不删除双指针。基本操作是,维护左右两个栈 \(A,B\),push back 就给 \(B\) push,删除就给 \(A\) pop(注意 \(A\) 的栈顺序和 \(B\) 是相反的)。当 \(A\) 为空时,将 \(B\) 中元素全部倒入 \(A\) 内。查询时直接查两个栈顶即可。一个元素只会被插入一次,倒栈一次,删除一次,复杂度线性。
现在有了 pop \(B\) 和 push \(A\) 操作,如果照着上面做,在 \(B\) 空的时候需要把 \(A\) 倒入 \(B\),来回倒栈复杂度会变成 \(\mathcal O(n^2)\)。这里需要人类智慧,每次一个栈空时,把另一个栈的一半倒进来。考虑这样的复杂度:把 \(f = |size(A) - size(B)|\) 看作势能,一次操作最多使得势能增加 \(1\)。倒栈时用 \(f\) 的代价将势能清 \(0\),因此复杂度仍然是线性。
这个东西在国外好像叫「单调 deque」,可能是因为大部分时候其维护的信息是 \(\min,\max\)。反正把这东西套进来就好了,鉴定为板子题。
- CF1209G2 \(3\color{ff0000}200\)
这么水的题想了快 1h 才会也是没救了,唉。
首先考虑 G1 的 \(q = 0\) 咋做,发现如果存在 \(a_l = a_r\) 那么 \([l,r]\) 内所有元素都相等。合并相等关系后,每个连通块的答案是元素数量减去出现次数的最大值。
对每种颜色找到其第一次和最后一次出现 \(l,r\),然后并查集合并 \(\forall i \in [l,r),(i,i+1)\)。差分优化即可。
首先注意到每个连通块是一个区间,且一个颜色的每一个出现位置都在这个区间里。所有连通块的大小之和显然是 \(n\),只需要求它们的区间 \(\max\) 之和。
将一个颜色的出现次数挂到这个颜色最左边的出现位置上以方便修改。注意到所有的断点都是序列上值为 \(0\) 地方,套路的把维护 \(0\) 改成维护最小值。线段树节点维护:当前区间被覆盖次数的最小值和答案。发现合并的时候,如果两边都是最小值,需要加上中间那一段的最大值,因此额外维护节点出现次数的最大值、最左侧最右侧最小值位置、以及这个位置到区间两端的最大值。直接合并即可。
一次修改只会影响两个颜色的覆盖和端点,开 set 维护即可。复杂度 \(\mathcal O((n+q) \log n)\),但是跑的超级慢,\(2 \cdot 10^5\) 跑了 \(1.5\) 秒,难以理解。
- CF633F \(\color{ff0000}2600\)
从 \(\color{ff0000}\textsf{TheScrasse}\) 的博客找到的水题。
引理:最优解里至少一条路径的一个端点是树的直径的一端。
证明直接调整:把某条的路径的一侧改到树的直径上显然不劣。
先把树的直径拎出来,求出直径上每个点向下延伸的最大深度和子树内的直径。
最优解其实只有个两种可能:从直径两端各自引出一条路径,或者从直径一端引出一段路径,并在被包含的这一侧的某个子树内部选择了子树直径(如果在外侧则还是可以调整到全局直径)。
求一下前后缀 \(\max\) 就好了,线性。
- CF1214H \(\color{ff0000}2800\)
从 \(\color{ff0000}\textsf{TheScrasse}\) 的博客找到的水题,但是 WA 了巨多发,假了巨多次,怎么回事呢。
考虑一条长度大于 \(k\) 的链,必定有 \(col_i = col_{i+k}\),也就是链上颜色是一段长为 \(k\) 的模式串的循环拼接。
我们希望简化问题,给大部分点直接找到一条长度大于 \(k\) 的链并直接确定其颜色。距离一个点最远的点总是直径,那么先找出直径并给直径染色后,如果某个点 \(u\) 距离较远的直径端点距离大于 \(k\),则该点颜色确定。注意这个过程中可能产生无解。
我一开始以为距离直径两端都不超过 \(k\) 的点随便染就好了,但是一直 WA on 18。注意到虽然一个点可能自身到最远点距离不到 \(k\),但是它可能被某个长度大于 \(k\) 的链覆盖带来限制。简单的实现办法是直接把点的颜色和距离其较远的直径那一侧的颜色涂成相同的,因为这种颜色一定是其子树内可能有的限制。
容易证明这样填涂的合法性:如果子树内部存在大于 \(k\) 的链,那么这个子树内到直径两端一定都存在距离大于 \(k\) 的点,而这会直接导致无解(\(k \ge 3\) 时)。因此如果有解,那么子树内不会存在长度大于 \(k\) 的链,证毕。
- CF773E \(3\color{ff0000}000\)
这也能 3000?
首先容易证明最优解一定是排好序后顺次操作。
注意到 \(f\) 的值一定是先一直减 \(1\) 直到 \(a_i = -i\),然后每次令 \(f \gets \min(f+1,a_i)\)。
在权值线段树上可以很方便的找到第一个 \(a_i= -i\) 的位置,因此第一部分贡献容易计算。考虑在权值线段树上维护第二部分贡献,相当于对每个权值 \(c\) 令 \(f \gets \min(c,f+cnt_c)\),写成 \((\min,+)\) 矩乘的形式 DDP 即可。
- CF855F \(3\color{ff0000}100\)
操作只有插入没有删除,因此一个位置只会从合法变得不合法。修改一个位置的合法性只会发生 \(\mathcal O(n)\) 次。
分正负维护权值,相当于做区间取 \(\min\) 区间求和,直接上 seg beats。把不合法位置全部看作 \(0\)。
区间修改合法性不容易,但是单点修改很简单。用 set 维护不合法位置即可。
- CF436F \(3\color{ff0000}000\)
把 \(a_i\) 的贡献拆给每一个 \(p\),问题可以抽象成前缀 \(+i\),查全局 \(\max\)。
考虑分块,散块直接暴力。注意到一个块在不断整块加 \(i\) 的过程中最大值位置在单调右移,因此可以维护每一个块最大值位置的 \(\mathcal O(B)\) 个分界点,查询时直接根据整块上打的 tag 找最大值位置。
考虑怎么在 \(\mathcal O(B \operatorname{poly}(B))\) 的时间复杂度内求出一个块的所有分界点:位置 \(i\) 想成为最大值,需要满足它大于前面所有数且大于后面所有数。我们不需要管后面的条件,因为这会在求后面的位置时考虑到。也就是说,位置 \(i\) 在时刻 \(k\) 成为前缀最大值的条件是 \(\forall j \in [l,i),\dfrac{a_j - a_i}{i - j}\)。所有可能的最优解都在上凸壳上,维护凸壳后三分即可。
总复杂度 \(\mathcal O(n \sqrt n \log n)\)。
ps:发现这个东西好像叫做“分块凸包”,唯一的区别是其维护凸包找最大值使用的是类斜率优化的单调队列,而不是我这里的暴力二分 😃
- CF407E \(3\color{ff0000}100\)
我一看题,这不是我们 ABC248Ex 吗,然后发现我还没写过 ABC248Ex。但是 ABC248Ex 你咋降紫了啊???
首先考虑一个序列可以重排成 \(d(d > 0)\) 等差数列的条件:所有数字模 \(d\) 同余,不存在相同的数字,且 \(\max - \min = (r-l)d\)。那么最多加入 \(k\) 个数字的限制相当于给第二个条件放宽到了 \(\max - \min \le (r-l+k)d\)。
\(d\) 非常让人难受,不妨把原序列每一个极长的模 \(d\) 同余段拿出来单独计算。这样我们就可以把 \(a_i \gets \lfloor \dfrac{a_i}{d} \rfloor\) 了,然后条件二相当于 \(\max - \min \le r - l + k\)。
考虑序列分治,预处理 \(mid\) 向两侧扩展的 \(\max\) 和 \(\min\)。枚举左端点,右端点被分成三个区间:\(\max,\min\) 都是左边的、\(\max,\min\) 有一个是左边的、\(\max,\min\) 都是右边的。简单推倒后发现这三个限制都是二维偏序形式。对于 ABC248Ex,直接跑二维数点即可;本题由于有一维的限制是前后缀,可以直接线段树二分。
这样的总复杂度就是 \(\mathcal O(n \log^2 n)\)。
当然这个题可以和 Good Subsegment 一样做,考虑扫描线扫描右端点,维护左端点的 \(\max - \min + l\)。我们需要找到最靠左的 \(\max - \min + l \le r + k\) 的 \(l\)。考虑维护 \(\max\) 和 \(\min\) 的单调栈,弹栈的时候做区间加减即可动态维护 \(\max,\min\)。然后线段树二分的复杂度是单 \(\log\)。
- CF2032F \(\color{ff0000}2800\)
神经病题。
考虑怎么判定胜负:如果第 \(i+1 \sim m\) 这个箱子后缀的胜负已经确定,
- 如果这个后缀状态先手必胜,那么第 \(i\) 个箱子谁先没有石子可拿就会获胜(因为会直接进入后缀的游戏状态)。
- 类似的,如果这个后缀状态后手必胜,那么第 \(i\) 个箱子谁先没有石子可拿就会输掉。
第一个游戏是 Anti-Nim 游戏,而第二个游戏是普通的 Nim 游戏。Anti-Nim 游戏的胜负判定是,
- 如果序列不是全 \(1\),那么先手必胜当且仅当异或和不是 \(0\)。
- 否则,先手必胜当且仅当 \(n\) 是偶数。
考虑对这个条件计数,显然需要一个 DP。先假设 \(a_i \ge 2,a_i = 1\) 是一些无聊的分讨,死吗出题人。
设 \(f_i,g_i\) 是 \(i\) 这个后缀先手/后手必胜的划分方案数,枚举下一段的端点 \(j\)。分类讨论 \(j \sim i-1\) 这局游戏(根据 \(f,g\) 判断是 Nim 还是 Anti-Nim)的胜负后转移给对应数组。
优化转移。每个 \(f_i,g_i\) 都是给所有前缀转移,\(g_i\) 只会给前缀异或和等于 \(pre_{i-1}\) 的位置转移,而 \(f_i\) 只会给前置异或和不等于 \(pre_{i-1}\) 的位置转移。用 map 维护即可。
\(a_i = 1\) 的情况是 trivial 的。
- CF2032E \(\color{ff0000}2400\)
读错题浪费 10min,失去了 rated 首 A 和 AK。
随便手玩以下发现给所有偶数位置操作可以给两个相邻位置 \(-1\)。再加一下就可以实现给相邻两个位置 \(+1\)。
那直接从 \(1\) 到 \(n\) 扫一遍一个一个消干净就行了啊。做完了吗?真做完了。感觉还没有有的 1600 难,乐。
- CF1178G \(3\color{ff0000}000\)
首先 \(b\) 数组没有修改,不妨直接把每个点的 \(|\sum b|\) 求出来。下面的 \(b\) 指的都是求和之后的 \(|b|\)。
单点改一个 \(a\) 相当于给子树内的 \(a\) 区间加。尝试把树拍到序列上,问题变为:区间加 \(a\),区间查 \(\max \{|a_i| \cdot b_i\}\)。
考虑分块维护,散块直接暴力,对于整块加打 \(tag\),那么查整块相当于查 \(\max \{|a_i + tag| \cdot b_i\}\)。套路的拆开绝对值变为 \(\max \{\max(a_i + tag)b_i,-(a_i+tag)b_i\}\),两者都是关于 \(tag\) 的一次函数,李超树维护。
时间复杂度 \(\mathcal O(n \sqrt n \log n)\),空间复杂度 \(\mathcal O(n \log n)\),稍稍调一调块长即可通过。
- CF2036G \(\color{ff8c00}2300\)
如果 \(a \oplus b \oplus c \ne 0\),那么对于任意 \(r \ge a\),\(ask(1,r)\) 的值总是不等于 \(0\)。二分出 \(a,b\) 后直接求出 \(c\)。
否则三个数字是 \(a,b,a\oplus b\)。由于三者的异或和不等于 \(0\),我们不能再利用单调性进行二分。注意到这三个数字的最高位不可能相同,且最大值和次大值的最高位相同。枚举一个 \(r = 2^i-1\) 后 \(ask(1,r)\) 就容易找到 \(\min(a,b,c)\)。现在就有了单调性,直接二分一个、求另一个即可。
两种情况的询问次数均为 \(2 \log_2 n+\mathcal O(1)\),可以通过。
- CF185E \(3\color{ff0000}000\)
首先思考一下 \(k = 0\) 怎么做,发现曼哈顿距离和 \(\min,\max\) 三者的结合是很困难的。
这里需要一个经典 trick:曼哈顿距离转切比雪夫距离,这样很容易发现答案就是 \(\max(x_{\max} - x_{\min},y_{\max} - y_{\min})/2\) 上取整。
如果一个人要去坐地铁,那么他肯定会去最近的地铁口。不妨先求出这个距离记作 \(d_i\)。
所有坐地铁的人里我们只关心 \(d_i\) 最大的,也就是说到如果一个 \(d_i = x\) 的人去坐了地铁,那么所有 \(d_i \le x\) 的人都去坐地铁不劣(这相当于不花费任何代价删掉了一些点)。所有坐地铁的人是 \(d\) 数组的一个前缀。
考虑枚举这个前缀,问题可以抽象成,有一个人的点集 \(A\) 和地铁站点集 \(B\),需要从 \(B\) 里选择一个点 \(P\) 和汇合点 \(Q\),使得 \(\max(\max\limits_{p \in A}\{dis(p,Q)\},dis(P,Q) + d_i)\) 的值最小。
仍然转成切比雪夫距离计算,二分答案 \(mid\),首先求出 \(A\) 集合内所有矩形的交集。这是不容易的,但是发现我们只需要关心四个坐标达到极值的点的交集,这四个矩形的交集等于所有矩形的交集。然后需要判断这些矩形的交的矩形,能否和另一个地铁站的矩形相交。地铁站矩形的边长一直在变,不妨反过来做,给这个矩形的边长增加 \(mid - d_i\) 并判断矩形内是否有地铁站。主席树维护。
复杂度 \(\mathcal O(n \log^2 n)\),常数很大但是 \(6\) 秒时限下应该没啥问题。
- CF1332G \(3\color{ff0000}100\)
这题是真没啥难度啊,我也不知道为啥我想了一早上才会做。
首先手玩一下发现答案 \(\le 4\),证明不难。那么只需要分别判定答案能否是 \(3,4\)。
先考虑 \(q = 1\) 咋做。答案为 \(0\) 当且仅当原序列不增或者不降,否则取转折点附近的三个答案就至少为 \(3\)。
判断答案能否是 \(4\) 是比较难的。答案为 \(4\) 一定是两个相交的上升/下降子序列插在一起。考虑其中间的两个元素,发现这个条件等价于 \(\min(a_2,a_3) < a_1,a_4 < \max(a_2,a_3)\)。
这时候应该有一个,可能是比较自然的想法:我钦定全局 \(\min\) 去当 \(a_2\),全局 \(\max\) 去当 \(a_3\),这样只要全局 \(\max,\min\) 都不在最边上就好了。如果全局 \(\max,\min\) 在最边上,那我直接把它删掉然后继续判断,直到 \(\max,\min\) 都不在边上,或者元素被删空。
很容易发现上面说的其实就是,判断是否存在一个子区间使得 \(\min,\max\) 不在边上。容易证明这个条件是充要的:充分性显然;必要性留给读者当作练习考虑调整法,把 \(a_2,a_3\) 直接调到 \([i_1,i_4]\) 的区间 \(\min,\max\) 即可。
用单调栈刻画这个条件:设 \(l_{1,i},r_{1,i}\) 是左右第一个大于 \(a_i\) 的位置,\(l_{2,i},r_{2,i}\) 是小于,区间 \(\min,\max\) 不在边上,等价于 \(\max(r_{1,L},r_{2,L}) < R\) 且 \(\min(l_{1,R},l_{2,R}) > L\)。注意我们没有必要区分 \(l_{1,2},r_{1,2}\),只需要关注他们的 \(\min,\max\)。以下记 \(r_i = \max(r_{1,i},r_{2,i})\),\(l_i\) 同理。
考虑扫描线,扫描右端点 \(r\)。对于同一个 \(r\) 可能会有多个合法的 \(l\),但是我们只需要关注那个最大的,因为更小的 \(l\) 一定没用,这是经典的支配点对思想。那么线段树二分求出 \(l\) 即可,时间复杂度 \(\mathcal O(n \log n)\)。
做完后再看一遍发现中间几乎没有任何难点(钦定 \(\min,\max\) 一步可能有些跳跃?这一步我想了比较久),是一道高质量普及组题!
- CF418E \(3\color{ff0000}100\)
打个表发现,\(\forall i > 3,a_i = a_{i \bmod 2+2}\)。
\(a_1,a_2\) 显然是好维护的,问题是 \(a_{3,j}\) 怎么求。先求出 \(a_{2,j} = c\),发现对于所有在 \(j\) 以前出现大于等于 \(c\) 次的颜色都会恰好造成 \(1\) 的贡献,其余颜色没有贡献。
于是问题转化为,给定一个序列,单点修改,求 \([1,x]\) 这个前缀里有多少颜色的出现次数至少为 \(c\)。
这个问题看着很难做,结合 \(3.5\) 秒的时限考虑根号分治。
- 对于 \(c \ge B\),全局出现次数大于等于 \(c\) 的颜色也只有 \(\mathcal O(\dfrac{n}{B})\) 种,直接对于每一种都判断一下即可。
- 对于 \(c < B\),考虑每次修改的时候记录下一个颜色的第 \(1,2,\ldots,B\) 次出现在哪里,那么 \(c < B\) 的查询就秒了。
分析复杂度:
修改 | 查询 | |
---|---|---|
\(c \le B\) | \(\mathcal O(B \log B)\) | \(\mathcal O(\log n)\) |
\(c > B\) | \(\mathcal O(B \log B)\) | \(\mathcal O(\dfrac{n}{B} \log n)\) |
可以用分块平衡复杂度,但是 \(\log\) 的常数都比较小,所以我猜他能过。
奇异搞笑了,阈值 \(3 \le B \le 25\) 的时候都能过,大了反而就过不了了,,,,,无敌了。
看了题解的证明,感觉挺有意思的:
- 第二行实际上是由一堆 \(1,2,\ldots,cnt_x\) 的子序列拼起来的。
- 考虑第三行的第 \(i\) 个 \(x\),其在第二行一定是 \(i\)。原因是第二行小于 \(i\) 的数字的第 \(x\) 个出现早于这个位置,大于 \(i\) 的数字晚于这个位置。进一步的,该位置应当是第 \(x\) 个 \(i\)。
- 第二行到第三行相当于做了一个求逆(第 \(x\) 个 \(i\) 变为第 \(i\) 个 \(x\)),那么求逆两次相当于啥也没干,因此第二行等于第四行。同理可得第 \(x\) 行等于第 \(x \bmod 2 + 2\) 行。
那么根据这个证明我想求 \(a_{3,i}\) 其实可以求出 \(a_{2,i} = x\) 后看一下 \(a_{2,i}\) 是第几个 \(x\),然后立刻得出 \(a_{3,i}\),发现这和我的做法其实是等价的。
- CF226C \(\color{ff0000}2400\)
\(0 \color{green}{+2} \color{black}= 2\)。
这难度真不是多了 1000?
经典结论题,斐波那契数列满足 \(\gcd(f_n,f_m) = f_{\gcd(n,m)}\)。
那问题相当于在 \([l,r]\) 内选择 \(k\) 个不同的数使得 \(\gcd\) 最大。
枚举 \(\gcd = d\),要判断 \([l,r]\) 内 \(d\) 的倍数是否达到 \(k\) 个,不难发现这个值是 \(\dfrac{r}{d} - \dfrac{l-1}{d}\),整除分块即可。
因为傻逼的 \(mod = 1\) 吃了罚时,哎。
- CF1238E \(\color{ff8c00}2200\)
\(2 \color{green}{+2} \color{black}= 4\)。
首先容易统计出 \(ct_{i,j}\) 表示文本串里从字符 \(i\) 移动到字符 \(j\) 的次数,最终代价是 \(\sum\limits_{i,j} |i-j|ct_{i,j}\)。
注意到这个形式是跟顺序有关系的,我们填数字的时候需要知道前面的顺序,这很不好。考虑把贡献拆碎,把 \(|i-j|\) 拆到每一条线段上计算,转移的时候就只需要考虑哪些元素在前面哪些元素在后面了。
\(m \le 20\) 显然在提示你状压,考虑将字符一个一个的填入,设 \(f_{mask}\) 表示当前已经填入的字符集合是 \(mask\) 的最优解,暴力 \(\mathcal O(m^2)\) 转移即可。卡卡常数就过了。
- CF375C \(\color{ff0000}2600\)
\(4 \color{green}{+8} \color{black}= 12\)。
感觉 CF1920F2 就是搬的这个题 /oh
题面已经告诉你了怎么判断点在多边形内,我们可以利用这个建图。
不妨考虑一个弱化一点的问题,只有一个关键点怎么做。我们从这个关键点往任意方向引一条射线,然后对每个格子设两个状态 \(f_{i,j,0/1}\) 表示当前边界线跨过射线奇数/偶数次的最小代价,对于跨过射线的转移就让 \(0/1\) 翻转。然后发现直接跑最短路更方便。
那本题直接把 \(0/1\) 换成 \(8\) 个物品的状态压缩就好了。
- CF722F \(\color{ff0000}2800\)
\(12 \color{green}{+5} \color{black}= 17\)。
首先枚举 \(x\) 的值,找到序列中存在 \(x\) 的行,这些行均摊只有 \(\mathcal O(nk)\) 个。
对同一个左端点右端点有单调性,双指针求解;怎么判断区间是否有解?不难发现这就是 ABC371G,维护对每个质数次幂的模即可。
这个东西不支持删除,谔谔。那就用经典的 Baka's trick 维护好了。由于 \(k\) 只有 \(40\),暴力合并的复杂度可以接受。
- CF547A \(\color{ff8c00}2200\)
\(17 + 0 \color{black}= 17\)。
注意到对一个东西操作 \(k\) 次会变成 \(x^kh+\dfrac{x^k-1}{x-1}y\),这个式子显然有长度不超过 \(m\) 的循环节 \(p\),那么所有解都可以表示成 \(k \equiv r \pmod p\),两个东西 excrt 一下就好了。
想一想都感觉难写,感觉要得 \(-1\) 分了。
复习了一下 excrt 的流程:考虑合并两个同余方程 \(x \equiv w_1 \pmod {r_1},x \equiv w_2 \pmod {r_2}\),相当于解同余方程 \(x = k_1r_1 + w_1 = k_2r_2 + w_2\),exgcd 出一组特解后调整需要 \(k_1 \gets k_1 + r_2\),而这会导致 \(x\) 的值增加 \(\operatorname{lcm}(r_1,r_2)\)(注意这里不是 \(r_1r_2\) 的原因是 exgcd 会先对方程取 \(\gcd\))。
- AT_keyence2019_e Connecting Cities \(\color{ff0000}2621\)
\(17 \color{green}{+8} \color{black}= 25\)。
这不是我们 Tree MST 的序列版本吗,太经典了啊。
经典结论:若有 \(k\) 个边集 \(S_1,S_2,\ldots,S_k\) 使得这些边集的并集恰好为原图的边集,则对每一个边集求 MST 后再求 MST 得到的是原图的 MST。
考虑序列分治,每次只考虑 \([l,r]\) 内跨过 \(mid\) 的边构成的边集。这样的第一个好处就是绝对值被干掉了,一条边 \((x,y)\) 的权值可以直接表示成 \(v_x + v_y\)。
考察这些边的 MST 的形态,根据 Kruskal 的贪心,发现一个左部点连接的一定是右边 \(v_y\) 最小的点,右部点同理。因此一次分治只会产生 \(r-l+1\) 条边,将这些边全部放在一起跑一个 MST 即可。\(\mathcal O(n \log^2 n)\)。
- AT_tdpc_graph グラフ \(3\color{ff0000}304\)
\(25 \color{green}{+8} \color{black}= 33\)。
远古题的评分还是太抽象了。
首先一个 SCC 内的点肯定全都走完,因此先缩点变成 DAG 上问题。问题抽象成,在 DAG 上找两条路径使得其并集包括的点尽可能多。
考虑费用流建模,拆点,连边 \((i,i+n,1,val_i),(i,i+n,1,0),(u+n,v,2,0)\),这样就保证了每个点只会在第一次经过的时候造成贡献。然后直接跑最大费用最大流即可。
- AT_tdpc_string 文字列 \(3\color{ff0000}122\)
\(33 \color{green}{+5} \color{black}= 38\)。
奇怪的条件让人考虑容斥,但我啥也没容出来 /ng
进行连续段 dp,一个连续段即为一个以后都不往里插入元素的区间。考虑如果某个 \(a_i = 1\) 其对应的决策有哪些:
- 插入在一个连续段的两侧,连续段个数不变,系数 \(2j\)。
- 合并两个连续段,连续段个数 \(-1\),系数 \((j-1)\)。
- 新开一个连续段,连续段个数 \(+1\),系数 \((j+1)\)。
上述操作全部合法,原因是在任意位置插入两侧元素都和 \(i\) 不同。
对于 \(a_i >1\) 的情况,我们考虑将所有 \(i\) 一起插入。分别枚举 \(i,j,k\) 表示有 \(i,j,k\) 个字符去做了对应的决策。这时候我们需要先合并连续段(如果先插入在两侧了可能导致无法合并),然后再进行其它操作(剩下两个互不影响)。系数是一些比较简单的组合数。
那么就做完了,复杂度 \(\mathcal O(nk^4)\),其中 \(n=26,k=10\)。注意一些神秘的实现细节,比如 \(a_1 = 0\)。
- \(^*\) AT_tdpc_concatenation 連結 \(3\color{ff0000}819\)
\(38 \color{red}{-1} \color{black}= 37\)。
很神秘的题。
字符串要求不本质不同是非常难处理的,很难记录一些状态来判断给一个串拼接上一个串后是否是本质不同的。
最核心的 idea 是直接对字符串进行 dp,将“是否能拼接”放进状态。定义 \(x\) 位置是字符串 \(s\) 的一个 分割点,当且仅当 \(s[1,x]\) 可以被拼接出来。那么最终要求的合法串即是存在分割点 \(n\) 的串。
直接设 \(dp_{n,i,j}\) 表示长为 \(n\) 的字符串,最后 \(8\) 位是 \(i\),且最后 \(8\) 位是否是分割点的状态压缩为 \(j\) 的字符串数量。转移很暴力:枚举下一位填 \(0\) 还是 \(1\),更新 \(i'\);然后看一下 \(k\) 能否和前面的字符串组成一个给定的模式串,如果可以,那么 \(k\) 会成为分割点。可以通过位运算技巧快速更新状态。
复杂度 \(\mathcal O(l2^{2|s_i|}\operatorname{poly}(n))\),可以通过。
- ABC135E Golf \(\color{ff0000}2772\)
\(37 \color{red}{-1} \color{black}= 36\)。
首先 \(k \bmod 2 = 0\) 时 \(x+y\) 的值始终是偶数,那么给定的 \(x+y\) 是奇数时肯定无解。
考虑我们先将和调整到 \((x+y) \bmod k\) 去,然后每次只加来调整到答案。
当 \(k \bmod 2 = 0\) 或 \(k \bmod 2 = 1\) 且 \((x+y) \bmod k\) 是奇数时非常简单;否则先给 \(x\) 加上 \(k\) 后再去做减法即转化为上述情况。
通过实践证明,只有在最优解是 \(2\) 的时候上述策略不是最优的。例如以下数据:输入 4 1 1
,我们会输出:
3 -1
-1 -1
1 1
而实际上再第二步就可以结束了,特判掉 \(ans = 2\) 即可 AC。
- AGC011E Increasing Numbers \(3\color{ff0000}314\)
\(36 \color{green}{+8} \color{black}= 44\)。
考虑一个贪心,每次直接挑选最大的小于等于当前数字的 Increasing Number 减掉。正确性感性理解。
怎么高效率的找?我们在考虑第 \(i\) 位时只关心第 \(i\) 位减的是 \(i\) 还是 \(i-1\),那只需要和 \(a_ia_ia_i\) 这样的数字比大小就好了,set 维护第一个非 \(a_i\) 的位置即可。如果减的是 \(i\) 那么没有影响,否则减掉的后面一定都是 \(9\),相当于减第 \(i\) 位并给后面加 \(1\),复杂度均摊正确。
- ABC128F Frog Jump \(\color{ff0000}2521\)
\(44 \color{green}{+2} \color{black}= 46\)。
什么唐题,,
观察一下选择 \(A,B\) 后走过的坐标点,发现形如 \(kA-kB\) 和 \((k+1)A-kB\)。也就是说,相当于两个从 \(A-B,A\) 开始,公差为 \(A-B\) 的等差数列。同时观察到到达 \(n\) 的那个一定是 \(A\) 开头的等差数列。
那我们不妨枚举 \(A-B\) 的值,然后根据 \(n\) 立刻得到 \(A\) 的值。注意到直接暴力累加复杂度就是调和级数,那直接暴力就好了。
- AGC015D A or...or B Problem \(\color{ff0000}2642\)
\(46 \color{green}{+8} \color{black}= 54\)。
首先有一个经典问题是,给定 \(l,r\),询问 \(\operatorname{OR}_{i=l}^r i\) 的值。这个问题的经典做法是,首先去除 \(l,r\) 的 LCP 位,然后找到最高位从 \(0 \to 1\) 的分界点,选择这两个分界点的答案即可达到 \(2^k\)。
回到原问题,考虑我们仍然去除 LCP 后找到最高位 \(0 \to 1\) 的分界点。对于分界点左边,实际上是一个 \(2\) 的某个次幂减 \(1\) 的后缀,其按位或的值不可能小于 \(l\) 也不可能大于分界点。对于分界点右边,可以看作一个 \([0,x]\) 的前缀,最大的或显然是 \(2^{\log_2x}\),再拼上左边的那个后缀或上一个最高位。简单分讨以下计算贡献即可。
- ABC134F Permutation Oddness \(\color{ff0000}2609\)
\(54 \color{green}{+8} \color{black}= 62\)。
仍然像 CF1238E 一样把 \(|p_i-i|\) 的贡献拆到每一条线段上,这样 dp 时就不需要考虑后面的一条线段到底要连到前面的哪里。
设 \(dp_{i,j,k,h}\) 表示填了 \(i\) 个数字,有 \(j\) 个 \(p_i \to i\) 的线段经过 \((i,i+1)\),有 \(k\) 个 \(i \to p_i\) 的线段经过 \((i,i+1)\) 且当前答案为 \(h\) 的排列数量。转移时考虑新填入的元素是谁指向了它和它指向了谁来更新 \(j',k'\) 的值。这样的复杂度是 \(\mathcal O(n^5)\) 的,比较难以通过(其实能过)。考察转移式子:
- 一个点向右侧连两条边,此时 \(j,k\) 都会 \(+1\)。
- 一个点向右侧连一条边,同时接受左侧向右侧的一条边,此时 \(j,k\) 都不变,乘上转移系数 \(j\)。
- 一个点向自己连边,此时 \(j,k\) 都不变。
- 一个点向左侧连两条边,此时 \(j,k\) 都会 \(-1\),乘上转移系数 \(jk\)。
不难发现 \(j,k\) 总是相等的,因此状态数可以砍掉一个 \(n\)。
感觉很多 dp 题的优化,可能把转移式子清楚的写在纸上而不是在脑子里口胡就能立刻知道怎么优化?
- \(^*\) ABC137F Polynomial Construction \(\color{ff0000}2467\)
\(62 \color{red}{-3} \color{black}= 59\)。
怎么是我不会的板子,呜呜。
学习了一下拉格朗日插值法,和如何利用拉插求出系数。
拉格朗日插值法
给定 \(n-1\) 次多项式 \(f(x)\) 的 \(n\) 个点值 \((x_i,y_i)\) 再给出 \(k\),求出 \(f(k)\) 的值。
直接给出构造:
\[f(k) = \sum \limits_{i=1}^n y_i \prod\limits_{j\ne i}\dfrac{k-x_j}{x_i - x_j} \]考虑带入 \(k = x_j\) 进行检验,发现只有 \(i = j\) 时乘法式的值为 \(1\),其余时刻由于 \(x_i - x_j\) 的存在导致整个分子的值为 \(0\)。
拉格朗日插值法求多项式系数
考虑怎么求出 \(f(k)\) 的系数,先对于每个 \(i\) 求出与 \(k\) 无关的常量 \(w_i = y_i \prod\limits_{j \ne i} \dfrac{1}{x_i - x_j}\)。不妨记 \(g(k) = \prod k - x_i\),那么 \(f(k) = \sum \limits_{i=1}^n w_i \dfrac{g(k)}{k - x_i}\)。
\(g(k)\) 可以直接暴力乘出来,而多项式除法由于分子只有一项,可以直接模拟,复杂度即为 \(\mathcal O(n^2)\)。当然如果不想写多项式除法,可以预处理前后缀的多项式乘积,但是代价是合并需要写 \(\mathcal O(n \log n)\) 的多项式乘法,可能在一些题上会被卡常,比如本题。
- CF39A C*++ Calculations \(\color{aa00aa}2000\)
\(59 \color{red}{+0} \color{black}= 59\)。
首先先把 a++
都变成 ++a
,然后这真不是直接 sort 一下就行了???
没看到系数可以等于 \(0\) 调了快 1h,这也太唐了吧。。。
- \(^*\) CF1386A Colors / P6682 [BalticOI 2020 Day1] 染色 \(\color{ff0000}2700\)
\(59 \color{red}{-3} \color{black}= 56\)。
太难了啊,从来没见过这么做的交互题。
询问次数基本就是一倍的 \(\log_2 n\),做法大概率就是倍增和二分。但是倍增和二分都很难做,原因是有“不能经过重复元素的限制”,而倍增和二分直接做,都有很大概率会经过重复元素。
我的一个想法是,以 \(mid\) 为对称轴进行询问,每次二分消耗两次询问以避免重复,尝试获得 \(67\) 分的部分分。然而这样甚至没有正确性,原因是最后剩下一个长度为 \(2\) 的答案区间时无法进行检验。
正解非常厉害。思路是,注意到 \(c = n\) 时一定需要把 \(1,n\) 问一遍,从最劣情况反推。即,把 \(c=n\) 时的询问情况反着模拟一遍,找到那个唯一的起点。注意到 \(c=n\) 时需要选择这个起点,因此大胆猜测直接从这个起点往两边交替的走就一定合法了,?
- CF809C Find a car \(\color{ff0000}2600\)
\(56 \color{green}{+5} \color{black}= 61\)。
神秘打表题,可惜因为忘了取模吃了两发,没能拿下 \(8\) 分。
直接开始打表,发现一个 \(1\sim2^n\) 的矩形是由四个 \(2^{n-1}\) 的拼起来的,其中左下方和右上方被加了 \(2^{n-1}\)。
首先拆成前缀询问,然后考虑直接暴力递归:注意到左上角的 \(2^k \times 2^k\) 矩阵贡献容易计算,剩下三部分直接递归。然后就 AC 了并且跑的飞快。双倍经验 储能表 的题解说,这样递归复杂度是 \(\mathcal O(\log n)\) 的,谔谔。
- \(^*\) AT_acl1_e Shuffle Window \(\color{ff0000}2500\)
不知道我在想什么,观察到了一些结论,但是还是不会做。
重要观察:如果任意时刻 \(x,y\) 出现在了同一个区间里,那么最终序列 \(x\) 在 \(y\) 前面的概率就是 \(1/2\)。这个其实比较好证明,因为你每一次同时包含 \(x,y\) 的 shuffle 都有 \(1/2\) 的概率让 \(x\) 跑到 \(y\) 前面,那考虑最后一次同时包含他俩的 shuffle 就行了。
有了这个观察以后就比较简单了。考察如果 \(x,y\) 不在一个区间里,不妨设 \(x\) 在 \(y\) 的左边。为了让 \(x,y\) 跑到一个区间里,那么唯一的限制是 \(x\) 进入 \(y\) 的区间以前不能被 shuffle 到序列的第一个元素去,也就是整个概率是 \(((k-1)/k)^{pos_y - pos_x}\)。
考察答案的变化量是多少,相当于枚举原序列的每一对 \(l,r\),给答案加上 \(\Delta_{l,r} p_{l,r}\)。把 \(p_{l,r}\) 在指数上的减法拆成一个除法就好,用树状数组维护。
- \(^*\) AT_acl1_f Center Rearranging \(3\color{ff0000}700\)
毫无头猪。可能是因为我和这题水平差太远了吧。
观察 1:每个数(不是每种数)只会被操作一次。
手画一些连续操作多次的情况就能发现是没有用的。
于是我们可以根据,这个数被操作的类型进行分类:它被放到了开头、结尾还是没有动过,不妨分别记作 LRM。那么不难发现 L 是一段前缀,R 是一段后缀,M 是夹在中间的区间。
数据范围很小,我们可以暴力枚举 M 的区间 \([l,r]\)。然后就有了要求 \(\forall i \in [1,l-1),p_i > p_{i+1}\) 和 \(\forall i \in [r+1,n),p_i < p_{i+1}\),其中 \(p_i\) 指的是 \(i\) 这个位置是第几次被操作的。
当然我们还需要考虑,每次操作只能移动中间的这个限制。考虑分讨每种数字的三个元素所属的类别,
- LLL 和 RRR 显然无解。
- 如果 L,R 没有同时存在,那么一定存在一个唯一的操作序列,且这个序列对 \(p\) 没有限制。
- 剩下只有 LLR,LRR 和 LMR 三种情况。
- 先考虑 LLR 的情况。如果第一次匹配了中间的 L,那么第二次必须选 R;否则如果第一次匹配 R,那么后两次必须选 L。这两种匹配方式由我们任选并增加 \(p_{l_2} < p_r\) 或 \(p_r < p_{l_2}\) 其中之一,但是他们共同限制是 \(p_r < p_{l_1}\)。LRR 和此同理。
- 考虑 LMR 的情况,我们可以选择先 L 再 R 还是先 R 再 L,根据选择会添加限制 \(p_l < p_r\) 或 \(p_r < p_l\)。
- 我们可以先跑出只保留 \(p_r < p_{l_1}\) 这类边的拓扑序,然后根据拓扑序选择剩下的边的方向。
等等,是不是忘了什么?我们忽略了剩下元素 M 部分的顺序关系。还有一个限制是,\(a\) 选择 M 的元素顺序拼接后应当和 \(b\) 的 M 区间相等。仍然分讨每种数字的三个元素类别:
- 对于除了 LMR 的所有类别,其要么不包含 M,要么选择是唯一的,只需 check 合法性。
- LMR 这个组合,最终 M 的取值根据先 R 后 L 还是先 L 后 R 可能保留 L 或 R 任意一个。因此位置关系会带来一些额外的限制,我们不能简单的根据拓扑序唯一确定。
- 考察我们的限制究竟是怎样的形式,发现我们需要给每个 LMR 确定左还是右,且如果一个选择了左可能另一个就必须要选择左或右,这其实是 2-sat。我们在判断两个 LMR 之间的关系的时候,需要考虑:连接后不能成环以及保留的 M 连接与 \(b\) 相等。
LMR 只有 \(\mathcal O(n)\) 个,故 2-sat 的复杂度不会超过 \(\mathcal O(n^2)\)。加上枚举区间的复杂度,总复杂度即为 \(\mathcal O(n^4)\)。至此这道题终于完成。
- AT_acl1_b Sum is Multiple \(\color{0000ff}1700\)
注意到题目所求式子即为 \(k(k+1) \equiv 0 \pmod {2n}\)。
显然 \(k\) 和 \(k+1\) 贡献的质因子是不同的,不妨对每个质因子分别考虑,对于每一个 \(p^c\) 都要有 \(k \bmod p^c = 0\) 或 \((k+1) \bmod p^c = 0\)。
那直接枚举一个子集 \(S\) 表示这个子集里的方程是 \(k \bmod p^c = 0\) 而子集外面的方程是 \((k+1) \bmod p^c = 0\),这是标准的 CRT,使用 atcoder::crt
即可。
- AT_acl1_c Moving Pieces \(\color{0000ff}1900\)
考虑费用流建模,对于非障碍物的格子 \((i,j)\) 向右和向下连一条流量 \(1\) 费用 \(0\) 的边。
但是怎么限制不能经过停留的棋子呢?不难发现棋子一定是一块一块的,因此限制每个点往汇点的流量均为 \(1\) 即可。
- AT_acl1_d Keep Distances \(\color{ff0000}2700\)
一开始思维卡死了,怎么想也不会优化。到了第三天换了一个思路立刻就会做了。dfs 做题真的不可取啊。
首先考虑只需要求最多选择几个数字咋做,有一个显然到不能再显然的贪心:从左到右,能选就选。可以很容易的用倍增优化到 \(\mathcal O((n+q) \log n)\)。
于是我的思路就在这里面卡死了啊,我想的是钦定元素 \(i\) 被选择,然后判断是否存在大小为 \(mx\) 的强制包含它的集合,但是这个很难进一步优化了。
找一找性质容易发现,最优解的第 \(i\) 个元素一定是原数组的一个区间,且区间两两无交(两区间交会立刻得到更有的解,矛盾)。然后又不难发现每个区间的左端点就是从 \(l\) 向后贪心的结果,右端点是 \(r\) 向前贪心的结果,四个倍增解决。
- CF643G Choosing Ads \(3\color{ff0000}200\)
终于卡过去了!不知道为啥 set 的 lower_bound 这么慢的,,
看到区间出现至少 \(p%\) 的数而 \(p \ge 20\),很难不想到随机化。随机一个数然后去查看其出现次数是否到达给定的值。
然而我们不能直接随机去估计数字的出现次数,这样在两个数字的出现次数较为接近的时候会出现问题。我们直接上 DS 维护!
修改只有区间推平,那么考虑颜色段均摊。珂朵莉树维护所有连续段,动态开点线段树维护每个颜色的出现信息。这样设随机了 \(k\) 次,复杂度就是 \(\mathcal O((n+q) \log n + nk \log n)\)。实测取 \(k = 80\) 就有比较大的通过概率了,取更大的 \(k\) 可能会 TLE。
有一些卡常,在查单点颜色的时候不要在 set 里二分,采用更加优秀的 \(\mathcal O(\sqrt n) - \mathcal O(1)\) 的分块可以大幅提升效率。
正解是扩展摩尔投票:考虑每次删除区间任意 \(k\) 个互不相同的元素,剩下的元素一定是所有可能的 \(k-\)
绝对众数。本题 \(k \le 5\),故线段树暴力维护区间的 \(k-\) 绝对众数,\(\mathcal O(k^2)\) 暴力进行合并即可。
- CF603E Pastoral Oddities \(3\color{ff0000}000\)
我们希望选择的边的最大值最小,那么贪心的选择尽量少的边。考察一个所有点度数都是奇数的图的形态,不难发现如果它存在环,我们就可以任意找一个简单环,然后删去这个环上的所有边,所以点度数的奇偶性均不变。因此首先得出结论,我们选的必然是一个森林。
考虑每个连通块,如果有奇数个点一定无解,原因是增加一条边会改变两个点的奇偶性。如果有偶数个点,类似 ABC345F 一样,可以证明一定有解。因此一张图合法的充要条件是,所有连通块的大小都是偶数。至此有显然的 \(\mathcal O(m^2 \log m)\) 做法。
注意到随着边数增加答案单调不增,考虑从后向前扫描线。考虑先去计算 \(m\) 条边的答案 \(ans\),如果存在大小为奇数的连通块则不断地加入边权为 \(ans+1\) 的所有边。注意边的生效时间是 \(i\sim n\)。该过程可以用半在线的线段树分治优化。时间复杂度 \(\mathcal O(m \log m \log n)\)。
我想了很久也不会用整体二分做这个题,但是题解里有整体二分做法,有时间学一学吧。
- CF765F Souvenirs \(3\color{ff0000}000\)
回忆了很久才设计出了一个正确的支配点对算法,哎。
首先不难发现这个题就是 P9678(做题笔记四中的题目)的序列版本。
所谓支配点对,指的就是如果 \(u \le u' \le v' \le v\) 且 \(dis(u',v') \le dis(u,v)\),那么点对 \((u,v)\) 一定没用。
考虑分治,强制区间经过 \(mid\) 转减法为加法。将所有点按照 \(dis\) 排序后,发现点 \(i\) 只会和 \(dis\) 小于等于它的点里的前驱后继连边。原因是如果 \(k < j < i\) 且 \(dis_k,dis_j \le dis_i\),那么 \((i,k)\) 一定会被 \((j,k)\) 支配。因此一个分治中心只会连出来 \(\mathcal O(len)\) 条边。
然后问题变成了给定 \(n\) 个子区间和 \(m\) 个询问区间,求每个询问区间包含的权值最小的子区间。这是经典问题,扫描线即可。
- CF1476G Minimum Difference \(3\color{ff0000}100\)
又是第一天想了很久不会结果第 \(n\) 天再看直接就会了,,NOIP 可没有给你第二天再想的机会。。。
发现这个询问及其难搞,而时限特别宽裕,那不妨直接暴力上带修莫队,这样我们就有了每个颜色在区间里的出现次数。
然而发现查询仍然很难做,很难找到一个数据结构可以支持 查询最短的和大于等于 \(k_i\) 的区间。但是注意到所有颜色的出现次数和是 \(n\),因此只有 \(\mathcal O(\sqrt n)\) 个不同的出现次数,直接把它们搞出来双指针即可。
如何找到这 \(\mathcal O(\sqrt n)\) 个出现次数?考虑每次修改的时候,如果这个颜色的新出现次数不是 \(0\) 就把它扔进 vector,然后每次查询的时候把出现次数是 \(0\) 的删掉。这样的均摊复杂度正确。
- CF1732E Location \(\color{ff0000}2900\)
原式即 \(\dfrac{ab}{\gcd(a,b)^2}\),那么考虑区间推平一段 \(a\) 后枚举 \(a\) 的因数 \(d\) 作为 \(\gcd(a,b)\),然后查询区间可以整除 \(d\) 的最小值。
考虑区间查询,查询可以分为整段和散段。整段在推平时直接记录区间答案,散段暴力计算,时间复杂度 \(\mathcal O(nd(v) \log n)\)。
- \(^*\) CF1687D Cute Number \(\color{ff0000}2900\)
注意到所有的好段是相邻完全平方数的前面一半,那么所有好段坏段都是长度是公差 \(1\) 的等差数列。
考虑枚举 \(a_1 + k\) 在哪个连续段,先让它在第 \(i\) 个好段的开头。关键性质是,注意到后面的每一个段的长度都长于 \(i\),因此 \(a_1 + k\) 从开头移到结尾时每个元素的段最多改变一次。因此的限制是,好段不能变成坏段,同时坏段必须变成好段,相当于限制了 \(k\) 的上下界。注意到 \(a_n - a_1\) 只有 \(10^6\),那么只会分布在 \(\mathcal O(\dfrac{v}{i})\) 个段里,复杂度调和级数。
- ARC187D Many Easy Optimizations \(\color{ff0000}2859\)
你们 ARC 签到题放 D 的吗,,,
最大/小化极差的套路是枚举其中一个并最大/小另一个。那么设 \(f_i\) 表示最小值为 \(i\) 时的最小最大值,显然 \(f\) 是单调不降的,且答案是 \(\min\{f_i - i\}\)。注意如果 \(i\) 并不存在于序列中也没关系,此时 \(f_i - i\) 一定不是最优解因为调大 \(i\) 一定不劣。
考虑加入一个数对 \((a,b)\),令 \(a \le b\)。那么应该有下面的变化:
因为 \(f\) 是单调的,区间取 \(\max\) 可以变为区间推平。直接上 odt 就结束了。
- ARC187C 1 Loop Bubble Sort \(\color{ff0000}2545\)
赛后花了 1.5h 才做出来,呜呜。有没有场切的大神教教是怎么快速想到统计每个 \(p'\) 的贡献,然后发现每个 \(p'\) 的贡献是 \(2^{\text{前缀最大值个数}-1}\) 的啊 /kel
首先考虑一个 \(p'\) 是可以达到的充要条件。首先需要有 \(p'_n = n\),必要性显然。充分性也显然。但是我们需要统计每个 \(p'\) 对应多少 \(p\)。
经过大量打表观察猜测可以发现一个 \(p'\) 对应 \(2^{\text{前缀最大值个数}-1}\) 个 \(p\)。
考虑 dp,设 \(f_{i,j}\) 表示填了 \(1\sim i\) 且前缀最大值是 \(j\) 的贡献和。直接 dp 是 \(\mathcal O(n^3)\) 的,前缀和优化就是 \(\mathcal O(n^2)\) 的。
一道从头套路到尾的套路题。
观察到对所有位置都操作一次相当于给所有数字 \(-3\),因此答案在模 \(3\) 意义下有单调性,即对模 \(3\) 相同的等价类答案单调。那么我们可以进行 \(3\) 次二分求解。
考虑如何 check 一个 \(x\),记 \(b_i = a_i - x\),首先要 \(b_i \ge 0\)。考虑记位置 \(i\) 操作了 \(h_i\) 次,那么不难有方程 \(2a_i + a_{(i+1) \bmod n} = h_i\)。这种问题的通用解法是根据第一个方程求出 \(h_1 = f_1h_n + g_1\),一直往后带入即可求出 \(h_n\) 的值,再往前反推即可。
注意最终 \(f\) 的系数可能达到 \(2^{n}\),但是注意到所有方程的解不会超过 \(10^9\),求这个方程在模 \(10^9+7\) 意义下的解即可。
奋战两小时拿下签到题。
首先考虑 \(a_i = 1,b_i = 2\) 怎么做,这和一般的汉诺塔问题基本相同。设 \(f(n)\) 表示子问题 \(n\) 的答案,考虑求解 \(f(n)\)。注意到 \(n\) 只能在空栈上移动,而任何东西都可以在 \(n\) 上移动,因此先把 \(n\) 移动到栈 \(2\) 位置,这需要先把 \(1 \sim n - 1\) 移动到栈 \(3\),代价是 \(2f(n-1)\),然后移动 \(n\),然后再把 \(1 \sim n -1\) 移动回来,总代价是 \(3f(n-1) + 1\),故 \(f(n) = 3f(n-1)+1\)。
回到一般情况,我们仍然从大到小的考虑元素。找到第一个 \(a_i \ne b_i\) 的位置,不难发现想要移动它 \(1 \sim i-1\) 必须叠成一个栈。因此总问题分为两个部分:求解 \(1 \sim i-1\) 合并为一个栈的代价,和求解 \(1 \sim i-1\) 这个栈拆成 \(b_i\) 的代价。
两个问题的解法基本相同,先考虑第一个问题怎么做。类似上面的,设 \(g(x,y)\) 是把 \(1 \sim x\) 堆在 \(y\) 的最小代价。考虑如果 \(x=2\),那么我们把 \(1\sim x-1\) 合成到 \(4-y\) 去,然后把 \(x\) 移到 \(y\),再把 \(4-y\) 的柱子移过来,有 \(g(x,y) = g(x-1,4-y) + 2f(x-1) + 1\)。\(x \ne 2\) 的情况是一些更复杂的分讨,但是核心想法相同。
第二个问题和第一个的唯一区别是移动和合并的顺序不同,改一下就好了。
时间复杂度显然是线性。
首先对题面做第一步转化:我们不关心最长公共子串的具体值,只关心它是否大于等于 \(k+1\)。而公共子串长度有单调性,因此我们只关心是否存在长度为 \(k+1\) 的公共子串。因此问题转化为:求有多少 \(s,t\) 满足存在公共子串 \(w\) 且不存在长度为 \(k+1\) 的公共子串。
注意到 \(2^{k+1} = 16\) 非常小,我们可以把所有的 \(2^{k+1}\) 个子串是否出现过压进状态。一开始我在想对两个串同时进行 dp,但是时空复杂度都爆炸。后来发现两个串的填充是基本独立的,我们只需要保证其最后拥有的 \(2^{k+1}\) 子串不交。
那么进行 DP,设 \(dp_{n,o,r,b}\) 表示长度为 \(n\),串的最后 \(k\) 个字符是 \(o\),串中是否存在了 \(w\) 的状态是 \(r=0/1\),串中拥有的所有 \(2^{k+1}\) 子串的状态是 \(b\) 的方案数,转移直接枚举填什么就行了,比上面那个题 连接 简单一万倍。
然后最终的问题是,求 \(\sum \limits_{i \& j = 0} f_ig_j\),这是套路的,考虑对 \(g\) 数组的下标反转(\(0111 \to 1000\)),这样限制就变成了 \(g\) 是 \(i\) 的超集,高维前缀和即可。
时间复杂度 \(\mathcal O((n2^k+2^{k+1})2^{2^{k+1}})\)。
- CF2038F Alternative Platforms \(\color{ff0000}2500\)
对两个序列的 \(\min\) 取 \(\max\) 是很难受的,因为 \(\max\) 对 \(\min\) 没有分配律。那么不难想到因为 \(\max(a,b) = a + b - \min(a,b)\),我们可以对两个序列分别求出答案后,再减去 \(\min\) 问题的答案。\(\min\) 对 \(\min\) 有分配律,因此这等价于对 \([\min(a_i,b_i)]\) 这个序列求答案。至此问题转化为单个序列。
首先给数组排序,考虑枚举集合大小 \(k\) 并枚举 \(\min\) 的值 \(j\),应当有 \(f_{k+1} = \sum \limits_{j=1}^n \dbinom{n - j}{k} a_j\)。这个式子可以卷积优化。
- CF2038I Polyathlon \(\color{ff0000}2500\)
最关键的观察是,对于一个固定的起点,考虑比较两个选手的字符串,找到第一个不同的位置,是 \(1\) 的会永远淘汰是 \(0\) 的。
因此枚举起点然后两两淘汰即可,复杂度瓶颈在于找 LCP。
比较简单的题,这题场上只过了 10 个是不是榜的问题啊??
首先记 \(s_a,s_b\) 是先后手的棋子总数,那么如果 \(s_a = s_b\) 则一定平局,否则 \(s_a > s_b\) 则先手不败,\(s_a < s_b\) 则先手不胜,因此我们要判定的其实只是先手能否胜/平。
先考虑第一种情况,我们希望先手获胜,这时候后手想要平局的唯一策略是一个棋子一直右移,让先手永远也追不上。这时候如果最右边的棋子是后手就直接结束了,否则先手需要在后手的棋子冲过最右边先手的棋子之前吃掉后手棋子。
注意到双方任意时刻都不会左移棋子,左移一定是不优秀的。考虑这么一个贪心:双方任意时刻都会选择自己最靠右侧的棋子向右移动。对于后手这个策略显然正确,因为这样移动就有更大概率可以冲出去。对于先手这个策略也比较对,因为移动右边的棋子有更大概率可以追上后手的棋子。
那么后手的棋子可以分为两部分:左半部分是放弃的,右半部分是向右移动并希望冲出去的,我们不妨枚举这个分界点在哪里,设分界点右侧有 \(x\) 个后手的棋子,那么根据上面的分析先手也只会动用前 \(x\) 个棋子去吃后手的棋。如何判断后手能不能冲出去?如果当前最右侧的棋子位置是 \(r\),那你其实只需要比较 \(\sum r - pos_a\) 和 \(\sum r - pos_b\) 的大小。这是因为一次吃棋后,我们仍然可以认为将两个棋子可以一起右移。
由于 \(\sum m_i\) 比较大,复杂度只能和 \(n\) 有关。当然这是简单的,直接双指针一下就是线性。
被诈骗了很长时间,火大!
重要观察:原图是广义串并联图。
由于边必须是直线,如果出现了同胚 \(K_4\) 的子图则必然画不开,因此原图不存在同胚 \(K_4\) 的子图,而这是判定广义串并联图的条件。
没有出现在 \(m\) 条额外边里的点一定是二度点,将它们全缩起来后点数就降到了 \(\mathcal O(m)\) 级别;缩合的新边系数可以用矩阵快速幂快速计算。
广义串并联图部分之前已经写过,在这里。
比 G 还离谱,这题应该过 100 个的啊,打这场比赛的选手是不是有点菜了???
首先每种颜色的合法性判断是独立的而且与颜色本身无关,考虑只有一种颜色该怎么做。不难发现我们一定会贪心的删去最大的,并且加入一个手牌里没有的最小牌,正确性显然。
颜色比较多的时候,问题在于可能存在一种策略使得从一个颜色里删去牌并加到另一个颜色里,这些决策是很复杂的,很难直接贪心解决,那么考虑 DP。
可能存在从后面拿牌给前面的情况,看似没法直接 DP。但是注意到,删去的牌可以插入任意颜色,即从不同颜色里删去的牌没有区别。考虑利用这个设计 DP:\(dp_{i,j}\) 表示考虑了前 \(i\) 种颜色,总共删除了 \(j\) 张牌时最少需要插入多少牌使得前 \(i\) 堆变得合法。最终 \(j\) 是合法的当且仅当 \(dp_{A,j} \le j\)。转移就是一个普通的背包。
时间复杂度 \(\mathcal O(n^2)\),可以通过本题。
- CF1601E Phys Ed Online \(\color{ff0000}2900\)
Div1 E 就这?感觉是个 Div1 B。
贪心的,每次只会在 \(l+pk\) 时刻激活票,而激活票的代价是 \(l\) 到它的 \(\min\),因此答案就是 \(\min(l \to l) + \min(l\to l + k) + \min(l \to l + 2k) + \ldots\)。
显然每个模 \(k\) 同余的类是独立的,分开计算,问题就可以转化为多次询问一个区间的前缀最小值之和。倒着扫描线维护单调栈即可。
- CF1583G Omkar and Time Travel \(\color{ff0000}3000\)
计算每个线段包含的关键线段个数 \(v_i\)。
考虑模拟这一过程,维护还没有经过的关键线段个数 \(w\)。如果当前刚刚通过时间隧道走完了一条线段,那么这条线段所有包含的线段都被清空。考虑此时的 \(w\):
- 如果 \(w \ne 0\),那么走完区间里所有线段也不可能变的合法,此时需要记录一个 \(g_i\),\(g_i\) 表示的是直接走过这个区间需要的时间。
- 否则最终的停靠点一定在这个区间内,给 \(w\) 加上包含的区间数量后直接暴力递归。
\(v_i\) 和 \(g_i\) 可以直接线段树扫描线求解,但是暴力递归的复杂度怎么分析呢?
很可惜,暴力递归的复杂度是 \(\mathcal O(n^2)\) 的,考虑区间 \(\{[1,n],[2,n-1],[3,n-2],\ldots,\}\) 就直接卡爆了!
观察我们的代码其实在干什么,是遇到一个关键区间给 \(w\) 减 \(1\),直到减到 \(0\) 后给 \(w\) 加上包含的区间数量。发现这其实等价于从最后一个区间往前一个一个扫!对于 \(l < now\) 一部分的处理可以树状数组解决。
- \(^*\) CF1621G Weighted Increasing Subsequences \(3\color{ff0000}200\)
差了几步观察。
首先考虑拆开贡献,对每个位置计算一下它被几个子序列包含。
那么不妨枚举位置 \(x\),一个合法的 \(y\) 需要满足 \(y\) 后面至少存在一个大于 \(x\) 的数字,考察这样的 \(y\) 有什么性质,首先它是一个后缀 \(\max\),因为下一个位置的后缀 \(\max\) 发生了变化。其次它是大于 \(x\) 的最后一个后缀 \(\max\)。
\(y\) 的条件显然有单调性,因此问题转化为求以 \(x\) 开头结尾小于 \(y\) 的上升子序列数量,但是这个很难。
-
性质 A:\(y\) 后面的数字全都小于 \(x\),因此答案就是 \(x\) 开头的所有上升子序列减去以 \(y\) 结尾的上升子序列。
-
性质 B:设 \(y\) 后面任意一个位置为 \(z\),则满足 \(a_z \le a_x < a_y\),因此一个后缀 \(\max\) 管辖的是一个区间,把每个区间和其对应元素分别拿出来求即可。
-
CF1535F String Distance \(3\color{ff0000}000\)
暴力直接 AC 了,哈哈。
首先注意到字符集不一样答案一定是 \(1337\),否则答案不会超过 \(2\):直接都排序一下就行了。按照每一个相同的字符集独立求解,由于串互不相同,我们只需要 check 每一个 \(i,j\) 的 \(f(i,j)\) 是 \(1\) 还是 \(2\)。
考虑什么时候答案是 \(1\),找到最左边和最右边满足 \(s_i \ne t_i\) 的下标 \(i_1,i_2\),我们排序的区间一定要包含 \(i_1,i_2\),并贪心的希望其不要包含 \(i_1,i_2\) 左边,因此排序的区间就是 \([i_1,i_2]\)。如果 \(s[i_1,i_2]\) 有一个是有序的那答案就是 \(1\)。
直接做是 \(\mathcal O(n^2 \log n)\) 的,在 \(|s_i|\) 很小的时候会 TLE,那考虑设计一个 \(|s_i|\) 比较小的时候的算法。考虑直接枚举区间 \([i_1,i_2]\) 是谁,注意这里需要要求两边相等,且这个相等段已经最长,这时候计算贡献。容斥来快速求解 \(i_1,i_2\) 两位上不能相同的限制。直接实现复杂度是 \(\mathcal O(n|s_i|^2 \log n)\) 的。
在 \(|s_i| \le 10\) 时跑算法二可以通过。
正解非常厉害,首先正解做的第一件事情是排序,这个看似没啥用的事情非常厉害:
- 对于固定的串 \(i\),和它离的越近的串 LCP 越长。
- 固定 \(l,r\) 时,如果一个区间内的串的 \([1,l-1]\) 都相等,且区间 \([l,r]\) 的无序集合都相同,那么 \([l,r]\) 有序的排在第一个。
考虑枚举一个 \(i\),只计算其后面的 \(j\) 的贡献。注意到 \(s_i\) 和后面字符串的 LCP 其实只有 \(\mathcal O(|s_i|)\) 个且每一个都形成一个区间,可以用单调栈维护这些区间。考虑每一个区间内的字符串,设这个区间和 \(s_i\) 的 LCP 是 \(p\)。根据上面的分析,如果某个 \((i,j)\) 排序 \([p+1,q]\) 是合法的,那一定是 \(i\) 有序而不是 \(j\) 有序,因此极长的有序区间就是 \(s_i\) 的 \([p+1,q]\)。我们需要统计这一段区间内后缀和 \(s_i\) 相同的有几个(由于字符集全都相同,后缀相同必定意味着中间的有序段的无序集合相同),trie 树维护即可。
时间复杂度是优秀的 \(\mathcal O(n|s_i| \log n)\)。
WARNING:以下内容为口胡,未经过代码验证。
哈哈,我是高贵的口胡狗屎分讨选手。
首先注意到 \(1,2\) 间的互相操作相当于异或,而 \(0\) 和其它数字的操作相当于单点推平。我们先以 \(0\) 为分界点将序列切成若干段。
考虑哪些情况下我们可以让答案是 \(1\):
- 如果有一段序列有偶数个 \(2\),那我们可以利用两边的 \(0\) 将除了这一段以外的地方全部推成 \(1\),最终答案为 \(1\)。
- 有一段有大于等于 \(3\) 个 \(2\)。可以这样操作:\([2,2,2,0] \to [2,1,0] \to [2,1]\),这样这一段和后面一段拼接后就有了偶数个 \(2\),条件一达成。
- \(0\) 旁边有 \(1\)。此时直接消掉 \(0\),条件一达成。
因此,如果出现了 \(0\),那么最终答案为 \(2\) 的序列形态只能是 \(\textbf{[1,1,1,\ldots,1,2,0,2,1,1,\ldots]}\)。
那很自然的想到直接分讨来求这个最小的字典序,唯一的问题是太太太太难写了。
考虑另一种套路的求字典序的方法,按位贪心。由上面的过程发现第 \(i\) 次操作不会操作 \(\ge 4\) 的位置,直接维护子串 \([2,0,2],[1,2],[2,1]\) 的数量和 \(0\) 的即可。 复杂度可能是线性。
- 感性理解复杂度挺对的。