[题解]THUSC2024总结
第一次参加 \(THU\) 的营,战绩惨不忍睹.
D1
T1
给出 \(d\) , \(n_1\cdots n_d\) , \(l\) , 求
其中 \(d<=10\) , \(n_i<=1e18\) , \(l<=1e9\) .
部分分1
$ d=1 $ 可以直接等差数列求和, 但是抽象输入模数搞得这分骗起来费点劲 , 除的时候要特判(我还开了__int128) , 写法极为抽象,不知道这5分有没有挂.
部分分2
对正解而言比较有用的部分分, \(l=0\) ,也就是说变成了单纯的求抑或和 , 考虑拆位求解.
对于每一位上的 \(i_j\)能贡献多少个1,多少个0,对答案的贡献单独计算即可.
正解
在部分分2的基础上,考虑加入 \(l\) 的限制,不难想到可以把异或和小于 \(l\) 的部分拎出来单独计算,再用整体的贡献减去这一部分.
于是可以把式子变成:
前一部分沿用刚才的方法就可以求得,后一部分很显然是一个以 \(l\) 为限制的二进制数位DP.
具体地,从 \(l\) 的高位开始逐个填数,考虑当前位是否被 \(l\) 限制,如果不限制就随便填,限制就成对填.因为 \(d\) 较小,可以直接 \(2^d\) 枚举填数.
填数过程中分别统计数量和加和就可以了.
复杂度 \(O(dlogn+2^dlogl )\)
听说可能略微卡常.
T2
给出一个字符串 \(S\) 与正整数 \(k\) ,要求构造一个最短的字符串 \(S'\),满足:
-
\(S\) 是 \(S'\) 的前缀
-
对于任意长度为 \(k\) 的小写字母字符串,都是 \(S'\) 的子序列
\(|S|=1e6\) , \(k=1e5\)
部分分1
这道题十分简单,而且部分分也给出了充分的引导,所以还是从部分分开始思考.
考虑从 \(S\) 向后补全字母.
第一档分给出了 \(k=1\) ,显然,这只需要 \(S'\) 包含所有小写字母就可以了,因此开个桶统计在 \(S\) 中已经出现的字母,把没出现过的补上就可以了.
部分分2
\(k=2\), 可以朴素地保证所有字母都可以分别作为长度为2的子序列的前一个字母和后一个字母就可以了.
因此,首先需要把原来的字符串中不存在的字母补全,保证所有字母都可以作为子序列的开头,然后在后面补上所有小写字母就可以了.
实现上,仍然是开桶统计,不过存在其他情况需要讨论.比如字符串 abcdefghijklmnopqrstuvwxyzaabb
,到z
的位置,已经满足了26个小写字母的存在,那么后面的aabb
是起后一个字母的作用,重新开桶补全后一部分的26个字母即可
同理,就可以得出不需补全的情况:两次开桶之后都填满了26个字母,这时字符串本身已经符合要求.
正解
在 \(k=2\) 的情况的基础上,就可以得到一个构造方式了:
对于原字符串 \(S\) ,开桶从前向后统计26个字母是否存在,一旦桶装满,我们称上次装满桶的位置到这次位置之间"包含一个字符集",同时把桶清空统计下一个"字符集".包含"字符集"的个数 \(k\) 就决定了能容纳 \(k\) 长度的子序列.
因此考虑在原来基础上补全"字符集",先把原串末尾不完整的"字符集"补全,再整个整个地向后补充.
为什么这样构造是正确的呢?
首先证明这样构造能满足限制条件"对于任意长度为 \(k\) 的小写字母字符串,都是 \(S'\) 的子序列":
采用逆证法:
假设对于有 \(k\) 个完整"字符集" 的串 \(S\) ,存在一个长度为 \(k\) 的串 \(T\) ,使得串 \(T\) 不是 \(S\) 的子序列.
考虑在 \(S\) 上从前向后匹配 \(T\) ,由于每个 "字符集" 包含完整26个字母,所以匹配过程中 \(T_i\) 必然可以在 \(S\) 上的第 \(i\) 个"字符集" , \(\Sigma _i\) 上匹配到一位 ,因此到 \(T_k\) 必然已经在 \(\Sigma _k\) 上完成了匹配,矛盾,因此原命题成立.
然后证明这样构造是最优的:
首先 \(S\) 为空串的情况下证明该构造最优:
在\(S\)为空串时,构造出的 \(S'\) 必然是 \(k\) 个"字符集"首尾相接,长度为 \(26k\),
假设存在一个更优的串 \(S''\),长度为 \(26k-1\) .
那么,在最优情况下, \(S''\) 拥有 \(k-1\) 个"字符集" ,且有一个缺少一个字母的不完整"字符集",考虑构造串 \(T\) 使得 \(T\) 恰好在 \(S''\) 的每一个"字符集"上匹配一个字符,并且无法与不完整的"字符集"匹配,此时\(S''\)不符合要求,矛盾,原命题成立
因此,为了构造最优的 \(S'\),一定是尽可能恰好构造出 \(k\) 个"字符集".
在原串基础上,首先使长度为 \(k\) 的串 \(T\) 最多按照上述方式匹配 \(k_0\) 位,考虑增加 \(k-k_0\) 个 "字符集",最优的构造一定是先利用原串后几个字符,补全一个字符集,再首尾链接补充字符集.
复杂度 \(O(|S|+26*k)\)
同时构造出的答案满足长度不大于 \(|S|+26*k-1\) ,可以保证在可接受数据范围内有解.
T3
给一棵树,边是有向边,有一些边确定了方向,另外一些边方向不确定,求一种确定边的方向的方案,使得树上任意一条路径的长度的最大值最小.求这个最小值.
部分分1
\(n<=15\) 直接 \(2^n\) 暴力.
部分分2
链的性质,可以通过讨论解决.
把链拉伸成序列,用 \(\mathrm{L}\) 代表向左的边 \(\mathrm{R}\) 代表向右的边,那么分开路径与路径的一定是形如 \(\mathrm{\cdots L R \cdots }\) 或 \(\mathrm{\cdots RL \cdots }\) ,在原序列上找到这些分界点,对于分界点直接不确定方向的边,尽可能一左一右地分布.
那末,如果形如 \(\mathrm{ L --R }\) 或 \(\mathrm{ L - L }\) 的情况,完全可以保证不确定方向的边独自成为一条路径,同时不对于左右造成贡献.但是形如 \(\mathrm{ L -R }\) 或 \(\mathrm{ L -- L }\) 的情况,需要存在一个边,对于其他路径有贡献,简单的讨论不宜解决.考虑在外层二分答案,贪心地在不超过答案限制下调整这条边的方向,如果还是超出了限制就说明答案不合法.
正解
我太菜了我不会.等我研究研究.
经lx大神指点现在我会了,原来我场上思路是对的(我太菜了
和部分分2类似,采取一个二分答案 \(l\) ,考虑二分答案时如何判定.
在链性质思路上拓展一下,想一种 dp .
对于树上的每一个节点,考虑保证从 \(u\) 经过的所有路径长度最大值不超过 \(l\) ,因此维护 \(f_u,g_u\),分别表示在子树 \(u\) 合法的前提下,从 \(u\) 出发的最长路径最小值,和到达 \(u\) 的最长路径的最小值.
对于子树的根 \(u\) ,首先,该子树有可行解的条件是把所有儿子划分为两组,一组边的方向连向儿子,一组边的方向儿子连向根,这两组的路径分别取 \(f_son\) 和 \(g_son\) ,需要保证这样产生的最大路径合法.
在此基础上,考虑更新根节点. 以更新 \(f_u\) 为例,用一个贪心的思想,尽可能让子树都取 \(g_son\) (因为这样从根到子树的路径为0),如果不能满足条件,就从小向大取 \(f_son\) 可以保证最优. 更新 \(g_u\) 同理.
T4
真正的工程题
提交答案题.给出10份伪代码,其中的命令包括:
-
\(\mathrm{input(a[x])}\) ,输入
-
\(\mathrm{a[x]=a[y]+a[z]}\),加法运算,\(x,y,z\) 可以重复
-
\(\mathrm{a[x]=a[y]}\),赋值
-
\(\mathrm{output(a[x])}\),输出
定义时间复杂度 \(T\) 为加法执行的次数,空间复杂度 \(M\) 为所有不同的使用 \(a[x]\) 的下标的个数
要求优化这些伪代码,使之时空复杂度降低至给定需求.
sub1-sub3
给出的伪代码实现的是乘法,实现方式是一个一个加.空间复杂度不限制,时间复杂度限制在 \(\mathrm{log}\) 量级.
改成龟速乘,每个点可以获得5-7分不等.
但是龟速乘不是很优秀,因为时间复杂度为 \(logn+popcount(n)\) ,也就是说二进制位上的每一个 \(1\) 都会增加常数,同时提供给我们的巨大空间并没有用上,继续优化可以参考sub4,不断在当前基础上自加,也就是不断进行左移操作,然后不断把 \(1\) 放在最低位,保留中间变量,这样可以用中间变量一次加好几个 \(1\) ,能减少部分常数.
sub4
sub4也是一个乘法操作,而且是一个比较优秀的实现,前8分要求的是在时间复杂度不变下减少空间复杂度,后2分要求在继续减少时间复杂度.
这个题空间复杂度部分完全可以手搓,因为保留的中间变量事实上只有几个起了作用,剩下的只是起倍增的作用,所以可以缩减掉没有用的中间变量.
这样就可以获得5分.
继续优化,发现有用的中间变量之中,有一些使用之后就没有用了,恰好可以充当接下来生成的中间变量,这样轮换着使用,可以优化到8分.
后面的不会,我太菜了.
后面的几个点有的直接提交原代码就能骗分,比较抽象.
D2
感觉不是很工程的工程题
是关于一个叫 WORDLE 的小游戏,(这东西还挺有意思).
sub1
给出大约 \(1e4\) 个单词作为可以猜的词, 大约 \(1e3\) 个作为可以作为答案的词(其余sub同理),然后要求模拟 WORDLE 游戏.
具体地,共 \(T\) 局游戏,每次输入猜词次数和答案,然后不断输入猜的单词,如果单词不合法(不在指定的可猜词中)需要返回Invalid,否则需要返回 WORDLE 游戏中单词上的标色和键盘上的标色,同时在猜对时返回猜词次数,猜错时返回正确答案.
没有难度,直接模拟即可.虽然说是工程题但是码量并不吓人.几个细节:
-
输入可能会有空格,需要getline或者getchar.
-
判断是否合法,我使用了字典树,其实暴力应该也不是不行,前三个点的时限都比较宽松.
sub2
在WORDLE游戏基础上,加入了一些困难模式的限定:
-
猜的词中,已经猜对的字母必须仍在原位
-
黄色的字母必须全部用上(用上的数量不少于标黄的数量)
要求模拟困难模式的 WORDLE 游戏.
就直接在sub1基础上增加限制即可,还是大模拟.
sub3
每次给出猜的词和回答的结果,要求给出还有多少种剩余的可能答案.
时间复杂度给的很宽松,仍然是直接模拟.
每一次猜词其实就是对答案增加了限制,考虑以下限制:
-
字母的最少出现次数:由黄色,绿色数量确定
-
字母的最多出现次数:由灰色决定,如果出现一个字母是灰色,他的上线就是本次出现的黄绿色的该字母数量
-
某位置上一定是该字母:由绿色决定
-
某位置上一定不是该字母:由黄色,灰色决定.
每次不断对限制加以修饰,然后暴力地跑一下就行了.
我用的是字典树维护答案字典,然后在上面dfs,能剪掉很多枝.
sub4
引入了信息量的概念,要求每次猜词后推荐一个信息量最大的猜法.
考场上挂在这里了,首先暴力枚举肯定是不行了,需要按照他给的思路,考虑用当前词完成对于答案集合的划分.所以场后胡了一个逐位构造的思路:当前已经绿色的不用改动,然后逐个枚举确定黄色字母的位置,剩下的就是枚举可行字母比较信息量了.
场后胡的不靠谱,我太菜了
还真是暴力.
在sub3的基础上做题,每次计算当前状态下所有的可能答案,然后枚举所有可猜词,对于每个猜词计算当前每个答案得出的返回结果,结果相同的归为一类,按照定义算信息量就行了.
sub5
首先按信息量贪心肯定是对劲的,可以拿到一些分数.
然后就是学习资料里给的两种优化方式,其中第二种明显比较好实现,即优先猜测在答案字典里的词,以期能尽量减少步数.
但是我卡t4了这个没法做,我太菜了
本文作者:youlv
本文链接:https://www.cnblogs.com/youlv/p/18188715
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步