AGC 做题合集 #8
代码啥的,可以看之前文章里面的网址,不想再打一遍了……
- "AGC019F Yes or No"[1]
- "AGC044D Guess the Password"[2]
- "AGC026E Synchronized Subsequence"[3]
- "AGC037E Reversing and Concatenating"[4]
- "AGC017E Jigsaw"[5]
- "AGC020E Encoding Subsets"[6]
- "AGC011E Increasing Numbers"[7]
- "AGC033D Complexity"[8]
- "AGC009E Eternal Average"[9]
- "AGC021C Tiling"[10]
AGC019F Yes or No
有 \(N+M\) 个问题,其中有 \(N\) 个问题的答案是
YES
,\(M\) 个问题的答案是NO
。当你回答一个问题之后,会知道这个问题的答案,求最优策略下期望对多少。答案对 \(998244353\) 取模。
\(N, M \le 5\times 10^5\)。
首先有一个 \(\mathcal O(n^2)\) 的 DP,考虑设答案为 \(f(n, m)\),那么我们的策略是猜 \(n, m\) 更大的对应的选项。
于是:
\[f(n, m) = \frac{n}{n+m}f_{n - 1, m} + \frac{m}{n + m}f_{n, m - 1} + \frac{\max(n, m)}{n + m} \]把 DP 转到网格图上面,横坐标是 \(n\),纵坐标是 \(m\),这个就是网格上面不断走路的问题。
考虑 \(y = x\) 这条线,比较特殊,同时我们考虑答案的线的状况,也就是我们从 \((n, m)\) 走到 \((0, 0)\) 的过程,而每一次我们选择的答案都是靠近 \(y = x\) 这条线的方向,通过手玩可以发现,我们无论如何,都会有 \(\max\{n, m\}\) 的收益,而唯一不确定的就是在 \(y = x\) 的时候我们的答案是蒙的,于是答案就是 \(\max\{n, m\}\) + 所有路径和 \(y = x\) 相交次数的期望 \(\times \frac{1}{2}\)。
后面这个是好做的,直接枚举在那个节点贡献,就是组合数了。 ↩︎
AGC044D Guess the Password
这是交互题。
有一个密码 \(S\),字符集为 \(\{a, b, \dots, z, A, B, \dots, Z, 0, 1, \dots, 9\}\),你不知道,长度都不知道(\(\le L\))。
每次,你可以猜一个密码 \(T\),交互器会告诉你 \(S\) 和 \(T\) 的最短编辑距离(每次可以删除、添加、替换一个字符,最少步数使得 \(S = T\)),在 \(Q\) 次询问后给出密码。
\(L = 128, Q = 850\)。
比较 easy 的 *3100,没有太多拐弯抹角。
长度都不知道就比较 **,考虑先解决长度问题。
我们直接丢一个长度为 \(L\) 的全
a
串,然后就知道a
的个数了!类似的,直接用 \(|\Sigma|\) 的次数不仅问出来长度,甚至问出每个字符出现次数了!问出了长度,我们就可以更进一步了,接下来的策略有几种(我想的):
- 考虑增量构造,每次插入一个字符,然后通过某种奇怪的方式确定这个字符位置。
- 考虑增量构造,每次确定下一个字符。
- 考虑将字符集从 \(\{a\}\) 不断拓展到 \(\{a, b\}\),直到到了 \(\Sigma\)。
经过推断后,可以发现最后一种方案比较有前途。
考虑 \(\Sigma = \{a, b\}\) 怎么搞,现在我们有一堆
a
和一堆b
,我们不断确定a
中间空隙的b
的个数,一个比较重要的性质就是如果在 \(T\) 中插入了一个对的字符(比如原来 \(T\) 是 \(S\) 的子序列,插入后还是 \(S\) 的子序列),那么编辑距离会减少。为此,我们可以类似归并排序一样,不断试着把空隙填满,如果填满了,就到下一个空隙中间。可以发现次数是 \(\mathcal O(cnt_a + cnt_b)\) 的。最后一步,我们知道启发式合并,它的本质是小的碰到大的,这个代价是小的大小,而这个题,我们可以每次选择 \(cnt\) 最小的两个串合并,于是次数就是 \(\mathcal O(L\log L)\) 了! ↩︎
AGC026E Synchronized Subsequence
有一个长度为 \(2 N\) 的仅由字符 \(\mathtt{a}, \mathtt{b}\) 构成的字符串,且 \(\mathtt{a}\) 的个数恰好等于 \(\mathtt{b}\) 的个数,都出现了 \(N\) 次。你需要保留一些字符,剩下的字符删掉。对于一个 \(i\),你可以保留从左往右数的第 \(i\) 个 \(\mathtt{a}\) 和第 \(i\) 个 \(\mathtt{b}\)。注意,对于这两个字符,只能同时保留或同时删掉,不能只保留其中一个。请你求出能得到的字典序最大的串。
\(1 \le N \le 3 \times {10}^3\)。
猜结论去了,人麻了。
考虑 DP,记 \(f(i)\) 表示目前考虑了 \(\ge i\) 的所有
ab
对的答案。对于第 \(i\) 对
ab
,我们分类考虑保留这一对ab
的最大答案,最后和 \(f(i + 1)\) 取 \(\max\) 即可:a
先出现,如果要保留这一对ab
,那么所有在ab
间的a
不能保留,因为比较拉跨,而对于b
如果保留了,还不如删了这一对,于是ab
间都不能保留,记b
后面第一对为 \(t\),那么 \(f(i) = \texttt{"ab"} + f(t)\)。b
先出现,这个是比较优秀的,而这个a
是比较劣质的,于是我们要在这个ab
之间尽量填满b
,直接取出中间所有b
\(\ge i\) 的对,取出后,我们发现,我们会触发连锁反应,于是一直加加加,直到加完了位置,然后继承后面答案。
答案就是 \(f(1)\)。 ↩︎
AGC037E Reversing and Concatenating
给定一个长度为 \(n\) 的只包含小写字母的字符串 \(s\) 和正整数 \(k\),求进行 \(k\) 次如下操作后:
将 \(s\) 和 \(s\) 的翻转拼接(\(s\) 在前)得到 \(t\),从 \(t\) 中截取长度为 \(n\) 的子串作为新的 \(s\)。
字典序最小的 \(s\)。
\(n\leqslant 5000,k\leqslant 10^9\)。
简单题,只要几个推论即可。
Lemma 1
有效的 \(K\) 不是很大,最多 \(\mathcal O(\log n)\) 。
考虑取出最小位置 \(p\),第一次,我们可以让 \(p\) 所在的地方为串的末尾,接下来,每一次,我们复制后,都能让 \(p\) 的个数 \(\times 2\),在 \(\mathcal O(\log n)\) 的时间内,直接全部是 \(p\) 了。
Lemma 2
在前 \(K-1\) 次操作中,我们选择的 \(T\) 满足 \(\overline{T}\)(\(T\) 反转后的字符串) 字典序最小。
最后一次暴力贪心即可。
为什么呢?首先,我们在前 \(K-1\) 次操作中,一定是优先让末尾的最小的字符数目尽可能多,其次,是为了让之后的选择更优,结合手玩可以发现,我们只要选择 \(\overline{T}\) 最小的即可。
综上所述,直接暴力即可,复杂度 \(\mathcal O(n^2\log n)\)。 ↩︎
AGC017E Jigsaw
题面还是看原题面吧,不太好描述。
rnm,不会处理连成环的情况/kk。
首先,对于 \(i \to j\) 这种连接方式合法当且仅当 \(B_i = C_j, D_i = 0\) 或者 \(D_i = A_j, C_j = 0\),这个启示我们建图,对于一个拼图,如果左边的 \(C_i = 0\) 那么代表值就是 \(A_i\),否则就是 \(C_i+H\),如果右边的 \(D_i=0\) 那么代表值就是 \(B_i+H\),否则就是 \(D_i\),然后左边的代表值向右边的代表值连一条边,容易发现,我们拼起来的方案就对应这若干条路径,每条从 \(i \le H\) 的点出发,不重复的经过每条边,最后在 \(j > H\) 的点结束。
我们可以根据度数判断是否有解,对于 \(i \le H\) 的点,一定是出度 \(\ge\) 入度的,对于 \(i > H\) 的点,一定是出度 \(\le\) 入度的。
最后一个问题就是会出现连成一个环的情况,这个可以判断一下对于一个弱连通的图是否是 "卡死" 的,即所有点的入度 = 出度,如果卡死了,那么就会形成环,否则不会。 ↩︎
AGC020E Encoding Subsets
我们定义一个
01
串的压缩是满足如下方式的字符串变化过程:- \(0\rightarrow 0,1\rightarrow 1\)
- 如果 \(A\rightarrow P,B\rightarrow Q\) 合法,那么 \(A+B\rightarrow P+Q\) 也合法(其中 \(+\) 代表字符串拼接)
- 如果 \(S=\underbrace{A+A+\cdots+A}_{n\text{个}(n\ge 2)}\),那么 \(S\rightarrow(A\times n)\) 也合法(其中
(
,)
,×
为字符,\(n\) 为数字,算作一个字符,即使其中有 \(0/1\))
我们同时定义 \(01\) 串 \(B\) 是 \(A\) 的子集当且仅当:
- \(|A|=|B|\)
- \(\forall B_i=1,A_i=1\)
现在给你一个 \(01\) 串 \(S\),问它所有的子集的合法变化结果数的总和为多少。
答案对 \(998244353\) 取模。\(|S| \le 100\)。
不懂诶,这种题简直就是写出即胜利,
为什么会是 *2900,为什么 luogu 对于 *3400 都要评蓝了而这个题还是黑,难道仅仅是题目标号带了 E?我不理解,但大受震撼。\(|S| \le 100\),这种数据范围告诉我们不要高估自己算法的复杂度。
于是可以考虑怎么优雅地写暴力。
我们记 \(f(s)\) 表示 \(s\) 字符串的答案,\(g(s)\) 表示 \(s\) 字符串必须表示为 \((t\times k)\) 的形式(其中 \(k >1\))的答案。
对于 \(g(s)\) 的转移,直接枚举循环节长度 \(len\),然后所有段合一起(对应位置取 \(\min\)),递归成 \(f(t)\) 的问题。
对于 \(f(s)\) 的转移,我们直接枚举 \(i, j\) 表示 \(s[0: i - 1]\) 是不能整花活(不能带有括号,显然方案是 \(2^t\),其中 \(t\) 为 \(s[0 : i - 1]\) 中 \(1\) 的个数)的,\(s[i : j]\) 是必须为 \(g\) 类型方案的,\(s[j + 1:]\) 是任意的,然后直接乘起来。当然,还要加上 \(s\) 压根不整花活的方案,\(2^t\)。
调完样例后发现跑得速度很快,手动测一测 \(n = 100\),发现能过?
然后就切了此题。此题唯一难点在于敢于写出暴力 DP。
因为没有看题解,所以解法有点奇怪,似乎有 \(\infty\) 倍常数,于是抢到了 Atcoder 的第 \(6\) 裂解。
刚看了一下大家的做法,顺便写一写,不过思路是差不多的,但是设计的状态比较巧妙。
设 \(f(s)\) 表示 \(s\) 的答案,\(g(s)\) 必须表示为单一字符或者由括号括起来的形式。
这样转移的时候就只要枚举一个分界点,并且常数减小了一些。 ↩︎
AGC011E Increasing Numbers
我们说一个数是“递增的”,当且仅当对于它的任意相邻的两位都有左边小于等于右边。
如 \(1558\), \(11\), \(3\) 是递增的,\(20170312\)、\(19260817\) 就不是。
现在给你一个数 \(n\),问最少可以被表示成几个递增的数之和。
比如 \(80 = 56 + 24\),\(2017 = 1349 + 668\), \(2019 = 1669 + 237 + 113\)。\(1 ≤ n ≤ 10^{500000}\)。
做出铜牌题好开心!
不过话说回来,铜牌题就这?对于递增的数,显然类似于洛谷 P2481 [SDOI2010]代码拍卖会,将其拆成若干个 \(000\dots0111\dots1\)。
接下来就比较 easy 了,从高到低考虑,显然会被低位的进位所影响,于是果断从低位到高位考虑。
假设我们现在有 \(t (t \bmod 10 = n \bmod 10)\) 个这种 \(111\dots1\) 的数字(并且,我们目前考虑到了第 \(i\) 位,这些数字后面的 \(i - 1\) 位都是 \(1\),前面的还没有确定),并且我们上一位给了我们进了 \(v\) 次位,如果我们这 \(t\) 个数这一位还是全部设为 \(1\),那么这一位的数就是 \(t+ v \bmod 10\),如果和目标不同,我们就只能通过让 \(x\) 个数这一位为 \(0\) 解决,如果最小的 \(x\) 要 \(> t\),那么我们就寄了,因为我们没有办法进行调整,否则我们直接让 \(t\) 减去最小的 \(x\)。为什么一定是最小的呢,因为这样我们会在后面有更多的自由度。
最后不断操作,如果到了第 \(0\) 位,考虑进位了 \(0\) 的数,如果是 \(0\),那么就是一个合法的方案了,否则就意味着我们的这种 \(000\dots0111\dots1\) 数过多了,可以考虑减少一下(我声称这是可以的,可以通过调整法,在 1 位置大量减少这种数,使得进到 0 的数为 0)。
注意上面的证明不是非常严谨,
但是作为做题人,只要到这一步就差不多了,在本机 Rand 几个数可以发现,一定存在一个分解点 \(lim\),满足有 \(lim\) 个这种数的情况下就是 OK 的,在 \(<lim\) 个数的情况下就会寄,多了就只会出现进位到 \(0\) 的数过多的情况。于是这个是有单调性的,可以直接二分答案。复杂度 \(\mathcal O(\log_{10} n \log_{2} {\log_{10} n})\)。
这就是传说中的复杂度带个 \(\log\log\) 吗。↩︎AGC033D Complexity
给定一个 \(N\) 行 \(M\) 列的字符矩阵。
我们定义一个字符矩阵的凌乱度为:
- 若这个字符矩阵中所有字符都相同,则凌乱度为 \(0\)。
- 否则,则考虑所有的沿水平或者竖直方向的直线,将字符矩阵分成两个不为空的部分,设两个部分的凌乱度分别为 \(a\) 和 \(b\),则整个字符矩阵的凌乱度为 \(\max(a,b)+1\) 的最小值。
请你求出,给出的字符矩阵的凌乱度是多少。
\(1 \leq N, M \leq 185\)。
以前的讲课题,知道大概思路,但是没有落实。今天 VP 的是否突然发现有这个题,于是编了编细节,然后写了写。
首先,我们有一个朴素的状态,\(f(a, b, c, d)\) 表示 \((a,b)\) 为左上角,\((c,d)\) 为右上角的凌乱度,而这个状态就是 \(\mathcal O(n^2m^2)\) 的,而转移还有一个 \(n+m\) 的复杂度,肯定寄了。
但是我们发现,这个题目的答案是比较小的(\(\mathcal O(\log n + \log m)\)),于是可以考虑将状态与答案互换,设 \(f(i, j, k, d)\) 表示从 \(i\) 行到 \(j\) 行从 \(k\) 列开始的凌乱度为 \(d\) 的最右边列是什么,而 \(g(i, j, k, d)\) 表示从 \(i\) 列到 \(j\) 列从 \(k\) 行开始的凌乱度为 \(d\) 的最下的行是什么。
以 \(f\) 转移为例,枚举划分方式,根据列切,那么就是 \(f(i, j, f(i, j, k, d - 1) + 1, d - 1)\) 了,如果按照行切,那么就要根据 \(g\) 来转移,也就是找到最大的 \(p\) 满足 \(g(k, p, g(k, p, i, d - 1) + 1, d - 1) \ge j\) 了,而在 \(i,j\) 固定而 \(k\) 递增的时候 \(p\) 是又单调性的,可以直接双指针求出。
复杂度 \(\mathcal O(n^2m (\log n + \log m))\)。 ↩︎
AGC009E Eternal Average
黑板上有 \(n\) 个 0 和 \(m\) 个 1,我们每次选择 \(k\) 个数字将其擦除,然后把它们的平均数写上去,这样一直操作直到只剩下一个数字,问剩下的这个数字有多少种不同的情况。
答案对 \(10^9+7\) 取模
\(1 \leq n,m \leq 2000,2 \leq k \leq 2000\)
保证 \(n+m-1\) 能被 \(k-1\) 整除。
联赛前写的题目,现在补一发题解。
首先,这种写平均数的题目,我们可以想到建树,一个点对于答案的贡献就是 \(\frac{1}{k^{dep}} a_i\)。
这个时候,比较 naive 的想法,就是直接考虑每次操作了的 \(1\) 个数,但是显然会寄,因为可以操作 \(k\) 个,于是产生了进位。
那么我们可以考虑最后的 \(k\) 进制小数,对于这个进行 DP,然后我们注意到对于原来的小数,每次进位后所有位置的数字和 \(\bmod~(k - 1)\) 的结果都是不会变化的,于是只要统计小数的数字之和 \(\bmod ~(k - 1)\) 和 \(m \bmod (k - 1)\) 相同的小数即可。
复杂度 \(\mathcal O(nm)\)。 ↩︎
AGC021C Tiling
能否在 \(n\times m\) 的棋盘上放 \(A\) 个 \(1\times 2\) 的砖和 \(B\) 个 \(2\times 1\) 的砖?如果能,构造出方案。
\(1\le n,m\le{10}^3\),\(1\le A,B\le 5\times{10}^5\)。
虽然没有看题解,但是并不是完全独立做出来的,因为 WA 到自闭了于是动用了魔法下了两个数据找到了叉点,于是过了。
首先,在 \(n, m\) 中间有一个偶数的时候,不难得到策略(假设 \(n\) 是偶数):
<><><><>^ <><><><>v <><>^^^^^ <><>vvvvv
而且最多浪费 \(2\) 个:
<><><><>^ <><><><>v <><>^^^^^ <>..vvvvv
其他都是利用最大化了。
对于 \(n, m\) 都是奇数的情况,我们可以照葫芦画瓢,再整一个类似的:
<><><><>^ <><><><>v <><><><>^ <><>^^^^v <><>vvvv.
最多浪费 \(3\) 个格子:
<><><><>^ <><><><>v <><><><>^ <><>^^^^v <>..vvvv.
然后交上去 WA 了 \(6\) 个点。
下载数据发现是第二种情况有问题。
手玩一下比较小的 case,然后发现在 \(3, 5, 5, 2\) 这种情况下,我们会:
<><>^ <><>v <>...
然后就寄了。
然后有更加优秀的做法:
<><>^ ^<>.v v<><>
这个启发我们再拼上这种方案:
<><><>^ ^<><>.v v<><><> ^<><><> v<><><>
这样两个方案综合,最多浪费一个格子。
注意,我们优先使用
<>
,于是<>
个数为 \(\max\{a, b\}\) (如果是 \(b\) 就交换行列构造,然后换回来),不然就会 WA 一个点。 ↩︎