To_Heart—题集——好多多的题
1.CF1860D
link && submission
发现自己并不会处理纯纯的 dp 甚至自己根本不会dp!
定义 dp_{i,j,k} 状态表示前 i 个字符有 j 个 0, 01 的数量减去 10 的数量为 k 。转移比较显然了,答案是 \(dp_{n,cnt0,0}\) ,其中 cnt0 是原序列中 0 的个数。注意 k 有正负。
2.CF1860F
首先很原神的一个转换是因为正整数所以可以转换成 a+bx,然后就可以看成一条直线了,特征值放进去就可以看成一个点。那么特征值只会在直线相交的地方改变,最多只会有 n^2 个点。枚举每个点,现在只需要考虑如何快速查询括号序列是否合法,将左括号看成 1 然后右括号看成 -1 再判断是不是所有的前缀和都大于 0 就行了!数据结构可以直接线段树扫描。
没有代码是因为官方题解的代码太妙了!
3.CF1864E
link. && submission
性质题,二进制后从高到低讨论什么时候会有人说不知道。发现当前这个人说不知道时一定是 s 的当前位为 1 且这个人所拥有的数的当前位是 1。接下来,如果另一个人的当前位也是 1 ,那么就直接到下一个 1 位,否则直接另一个人就已经知道谁大谁小了。粗略地发现两个数之间比较的次数也许和二进制后 1 的个数有关。
接下来换个方向思考问题,考虑当 Alice 拿了某个数 a 的时候,其他数合起来需要多少的次数。假设另一个数是 b 。我们把这两个数二进制串分别写为 SA 和 SB ,S 是他们相同的前缀。不难发现在S之后的第一个不同的地方两者就能分出大小。因为是前面相同的部分的长度会对两个数的比较产生贡献,并且不会结束比较,这引导我们考虑 trie 树。特判两个数相同的情况,接下来只需要考虑两数不同时的比较次数。剩下两种情况,第一种是 a>b,此时结束的位置一定是 a 从左到右第一个 b 没有的 1 的位置。然后根据奇偶此时可能是 Alice 或者 Bob 做出选择,可以发现 Alice 做出选择时次数需要多一次。然后讨论下去就好了。发现一个数与其他数的快速比较可以放在 trie 上,所以整体复杂度就做到了 \(\mathrm{O(nlog_n)}\)。
可以结合代码思考!跟官方题解不太一样就是了。
4.CF1864F
挺神仙的一种离线方式。
首先将值域在 [l,r] 的所有值拿出来。因为区间无交所以一定要从小到大把所有的值变为0,而且每种权值至少删一次,接下来考虑额外贡献。考虑如果两个相同的数中间出现比它们小的数,这两个数就无法同时删除。推广一下,每相邻的两个相同的数之间出现了比他们小的数就会增加贡献。如果说我们查询的区间是 [1,r] ,那么就很可做了。因为这时候我们只需要考虑每个小于 r 的数是否存在如上的额外贡献,但是发现查询区间有个左端点 l。你看啊,我们一开始发现查询区间为时 [1,r] 很可做,那么多了一个 l 的影响无非是 [1,l-1] 的数不在出现在序列上。第一个影响是这段的数不再对答案产生贡献,第二个影响是对于 [l,r] 的数不能再因为 [1,l-1] 的数产生额外贡献。对于第一个影响,我们可以将查询拆成 [1,l-1] 和 [1,r];对于第二个影响,如果两个相同的数之间比它们小的数大于 l 那么才能产生贡献,所以我们只需要用一颗支持查询区间最大值的线段树看看这个区间的最大值是否满足就行了。然后贡献可以看成是个前缀形势,需要快速修改和查询,再写个树状数组就好了。
5.CF232E
猫树!喵喵喵!所以叫猫树是因为很喵喵嘛/se 这道题算是猫树板子题了,所以这道题的重点是讲猫树!
猫树本质上是预处理,可以在 O(nlogn) 的时间复杂度里预处理出一个序列所有 [l,r] 的信息,要求维护的信息有结合律。查询是 O(1) 的。考虑建一棵线段树,那么任意一对 [l,r] 区间在线段树上最后一次出现一定是在线段树上叶节点 \(P_l,P_r\) 的 LCA 节点所在的区间上。这样就能保证每个区间会且仅会被线段树上的一个节点记录。对于每一个表示 [l,r] 的节点,如果我们能在 O(r-l+1) 的时间复杂度里面处理完这个节点所标记的所有区间,那么我们就能在 O(nlogn) 的时间复杂度以内完成所有区间的预处理。
考虑每个区间 [l,r] 的 mid,预处理出所有 [l,mid-1] 到 mid 的区间的信息 和 mid+1 到 [mid+2,r] 的所有区间的信息,然后把同层的节点合并成一个 [1,n] 的区间,在查询的时候找到 l,r 然后直接结合 [l,mid] 和 [mid+1,r] 的区间就好了(你可以直接吧这两个区间的信息挂在 l 和 r 的上)。 查询的时候不用关注 lca 是什么,只用关注 lca 的深度就行了。位运算的方法是 \(log_2x-log_2( x\oplus y)\) 就行了。
然后你发现这道题暴力查询,对每一列使用一个bitset优化的时间复杂度是 \(\mathrm{O(\frac{qnm^2}{w})}\) 的,但是发现可以猫树!所以时间复杂度被平衡为 \(\mathrm{O(\frac{nm^2logn}{w}+\frac{qm}{w})}\) 的。厉害吧。
666.[THUPC2022 决赛] 想象
link
整个花活(
但是但是我们 OIer 不能只做死题!要关心时事!
6.CF1720D2
link && submission
对 0-1trie 有了全新的认识。
这道题首先你他妈得知道看到异或想 0-1trie,因为我实在不知道怎么自然的想到这个。想到 0-1trie 后启示性的将问题转换到二进制每一位考虑。然后题目给的有点复杂,把 b 提出来后其实就是要满足:\(\forall i,j \in b,i<j,a_i \oplus j< a_j \oplus i\)。
那么你分到每一位上,发现可以用一个二元组存一下当前位的两个变量的状态,然后每一位的比较就很简单了。但是但是你这样直接建 trie 是四叉的!而且查询有可能会走两个方向!!
然后你发现什么情况需要再次递归,那么就是 \(a_i \oplus j= a_j \oplus i\) 你发现可以移项! \(a_i \oplus i= a_j \oplus j\) 这玩意儿莫名戳中我的笑点乐死我了,哈哈哈哈哈哈哈哈哈好好笑啊操。
所以异或相同的两种情况可以合在一个节点上,然后每个节点放两种状态进来分别的答案就好了。
7.CF1771F
link && submission
这题也是个好活。你看到奇数想到什么?没错,异或!但是直接用原数异或会有很多奇奇怪怪的错误,所以给每个值一个hash值然后在主席树上二分值域就好了。
8.AGC024E
link. && submission
神仙题!不知道是不是!但是我是常例的不会 dp !
换一下顺序,考虑怎么由 \(a_i\) 生成 \(a_{i+1}\),那么新插入的数一定要大于它后面的数。 假设现在我们生成到了第 \(i\) 个序列,用了前面 \(j\) 个数,然后现在插入的依然是 \(j\)。因为必须大于所以插入的位置是有限的,所以我们还需要限制一个 \(k\) 表示当前还可以插入的位置。dp 状态可以表示出来了 \(dp_{i,j,k}\) 表示生成到第 i 个序列,用了前 j 个数,有 k 个数小于 \(j\) 。这种定义下发现可以插入的位置是 k+1 个因为你可以在末尾补一个。
考虑转移。如果当前不插入 j 的话,那么相当于有一个本来可以插的位置但是你不插了,所以就是 dp[i][j][k-1]+=dp[i][j][k]。值得注意的是如果 k=0 那么说明当前这个数已经考虑完了所以转移是 dp[i][j+1][i]+=dp[i][j][k];如果插入 j ,那么一共有 k+1 个位置可以插,而且注意到插入后下一个数能插入的位置的数量并不会改变,所以转移是 dp[i+1][j][k] = dp[i][j[k]*(k+1)。
答案就是 dp[n][m][0]。
9.AGC040E
link && submission
妙!妙!
如果只有操作一,你发现答案就是 \(a_i>a_j\) 的数量。现在有两种操作了。那么考虑每一个位置的数由操作一贡献 \(b_i\) 和操作二贡献 \(c_i\) 。设置状态 dp[i][j] 表示第 i 个数,\(b_i=j\) 时的最小代价。显然的转移是:$$dp_{i,j}=\min dp_{i-1,k} +[j<k] +[j<k+a_i-a_{i-1}]$$ 是 \(\mathrm{O(nV^2)}\) 的,V 是值域。
这道题有两个 nb 的地方,第一个就是一开始的分拆转换,第二个就是优化 dp。\(V^2\) ——> \(logV\) 是什么概念?!第一个性质是 \(\mathrm{dp[i]}\) 这个序列是递减的,这个很显然;第二个性质比较隐藏,考虑用更新 \(\mathrm{dp[i][a_i]}\) 的最优点更新 \(\mathrm{dp[i][0]}\),那么真正的 \(\mathrm{dp[i][0]}\) 肯定不大于这个,所以就满足 \(\mathrm{dp[i][0]\le dp[i][a_i]}+2\)。
你发现整个序列 \(\mathrm{dp[i]}\) 的值被分成了三段。所以我们只需要对这三段 dp 就好了。找到每一段的位置可以固定左端点二分右端点。
10.ARC156D
link&&submission
好题。充分利用了异或的性质。
因为异或,所以一旦一个数出现两次就没了,所以如果所构造的序列 p 不是回文的,那么把这个 p 反过来就能让 \(\sum a_{p_i}\) 再出现一次,异或了就没了。所以能对答案产生贡献的序列一定是回文的。那么你直接每次截一半的贡献*2,再特判一下奇偶就行了。\(\mathrm{dp[i][j]}\) 表示长度为 i 的序列求和加上 j 的所有序列的异或和 ,这么定义的原因是奇数的回文中间可能会多出一个 j 来。
这做法才真正意义上对得起它所谓的 D 题难度,因为没什么深奥的理论了。