2024.3 做题纪要

省选考的一塌糊涂,学了约半个月 whk。

模拟赛不知道有没有比较需要写的,看情况,更新可能很少,主要用来写题解。

2024.3.16

算是开始集训了,虽然还是不知道怎么训更高效点但是先暂时随便训训吧。实在想不到好办法。

把省选 D1T2 和 D2T1 补了。有点绷不住了。两题都没看题解就改完了,怎么说呢,略有点弱智吧。虽然以我场上的状态确实是做不完这两题了。

P10218 [省选联考 2024] 魔法手杖

考虑二分答案,假设最后答案 \(\ge v\),那么所有值都需要 \(\ge v\),注意到 \(a_i + x \ge a_i \oplus x\),也就是说操作后一定更大,那么首先我们必须满足 \(a_i + x \ge v\),否则答案一定不可能 \(\ge v\)。那么我们现在问题变成了,将所有 \(a_i \oplus x < v\)\(i\) 加入 \(S\) 集合,然后看是否能满足 \(m\) 的限制,同时必须满足 \(x \ge v - \min a_i\)。我们记 \(v - \min a_i = l\)

在 Trie 树上进行考虑,我们要把所有值全部异或一个 \(x\),也就是将若干层的所有结点的左右儿子互换。考虑一层一层考虑,我们要统计的是 \(a_i \oplus x < v\)\(b_i\) 和,假如当前 \(v\) 最高位为 \(1\),那么注意到所有 \(a_i \oplus x\) 的最高位为 \(0\) 的值全部会被统计,也就是说左子树会全部统计上,然后再往右子树递归,同理,如果 \(v\) 最高位为 \(0\),那么右子树全部不会统计,然后再往左子树递归。注意到我们每次只需要往一端递归,所以这样我们就可以得到子任务的结构了。我们可以确定 \(x\) 当前这一位填什么,如果填 \(1\) 那么就是交换当前点的左右子树,然后再根据 \(v\) 的值向左或向右递归。对于 \(x \ge l\) 的限制,我们类似于数位 DP 的处理方法,记录一下当前是否卡下界即可。那么这样我们就可以得到一个单次 \(O(n k)\) 的 DP 了。加上外层的二分,总复杂度 \(O(n k^2)\)

二分答案看起来就不好优化,考虑优化 DP 的部分。发现问题在于 Trie 树上的二度点太多了,假如我们把它的二度点都压缩起来,点数就是 \(O(n)\) 的了,于是我们只需要把压缩 Trie 搞出来,然后每次能够快速计算一条链上的答案即可。计算链上的答案稍微比较麻烦,因为同时有左右子树、\(v\)\(l\) 与卡下界这四个信息,不过通过一些精细的位运算处理可以快速计算出这一部分的答案。于是总复杂度就是 \(O(nk)\) 的了。

赛时我写了个 \(O(k(n+k))\) 的做法,我本来以为这和 \(O(nk)\) 是同阶的,但是没想到被出题人阴了一手,具体看 警惕 OI 中的多测陷阱。大概就是因为卡下界的信息处理太麻烦了,我赛时就想到,卡下界的只有一条链,那么把卡下界的暴力 DP,不卡下界的再去考虑就简单很多了,这样单次 DP 是 \(O(n + k)\) 的。唉,抽象了。虽然就算是不卡这个我赛时代码常数仍然很大,赛后又加了一些优化加剪枝才能把大样例跑进 1.5s。

P10220 [省选联考 2024] 迷宫守卫

赛时想这题的时候大脑确实不太活跃,跟我打模拟赛似的,过去半场了脑子才清醒 😕 开场两个小时才把 \(O(4^n)\) 的做法写明白,然后就不敢再浪费时间想 \(O(2^n \mathrm{poly}(n))\) 了,想着赶紧把 T2 T3 做做,然后结果最后 T2 冲了个做法假的还没写完,写完了起码也有 40 分呢呃呃,流汗了,T3 还一眼没看

总之这个题剩下部分省选完了晚上在床上就想明白了。哈哈。

字典序肯定考虑按位贪心,常见的按位贪心方法是每次二分这一位填啥,但是这题的条件导致并不好二分。(考场上在这里吊了好久)但是仔细观察一下,选择第一个数后,第二个数就已经固定了,第三个数只有两种选择,于是我们大胆猜测,需要决策的数的数量很少。事实证明,这个数是 \(O(2^n)\) 级别的,可以大致写出一个递推式子 \(f(n) = 2^{n-1} + 2f(n-1), f(0) = 1\),容易归纳证明 \(f(n) < 2^{n+1}\)。于是我们可以直接暴力枚举每一位填什么然后看合不合法就行了。

先考虑填一个数怎么计算最小花费。如果第一个数选择的是 \(v\),那么说明所有 \(< v\) 的数就都不能够到达,考察给定的操作是钦定向左子树走,这可以看作是删除右子树,那么我们问题就是删除若干右子树使得所有 \(< v\) 的数全部被删除。这个容易树上 DP 得到。

然后考虑如果确定了多个数怎么办,这时候再从删除的角度考虑就不好了,因为还可能继续往右走。此时我们分成两种情况:假如某一个点左子树和右子树都有确定了的数,那么我们只需要保证左子树第一个到达的数小于右子树第一个到达的数,或者购买当前节点使得强制先往左走。假如先往右走但是右子树第一个到达的数还比左子树大,那么这种情况是不合法的。假如某个点只有左子树或者只有右子树有确定的数,那么我们要做的就又是需要将另外一个子树内的所有小于这个数的点全部删除,或者钦定向左走。这样我们就可以得到一个单次 \(O(2^n)\) 的 DP 了。

考虑优化这个 DP,注意到每次我们进行决策的时候,我们实际上是从一个子树内选择一个,也就是只会改变这个子树内的情况,于是我们每次只进行子树内的 DP,然后再把根链上的 DP 值更新一下就行了。不过每次 DP 整个子树复杂度还是很高,注意到我们 DP 的实际上是将所有 \(< v\) 的值删除,那么我们可以从小到达枚举子树内的点,然后每次相比上一次就只需要多删除一个点,这样只需要更新一条根链就可以了,这样进行一个子树内的决策的复杂度就是 \(O(n \cdot siz)\),这样总复杂度就是 \(O(n 2^n)\) 了。

2024.3.19

P10198 [USACO24FEB] Infinite Adventure P

打这场 USACO 的时候完全不会做。菜菜。不过确实好题。

这种东西看起来就会想图上倍增,但是有一个致命的问题就是如果你从一个 \(t_i\) 小的地方跳到 \(t_i\) 大的地方,那么你就无法得知你在这个点的下一条出边是什么了。于是直接倍增肯定是行不通的。

但是观察到,\(t_i\) 变大只会变大 \(O(\log m)\) 次,那么也就是说我们可以倍增跳到下一个 \(t_i\) 比当前大的,这样倍增跳复杂度也是可以接受的。

于是我们有一个朴素想法:设 \(f_{u, t, k}\) 表示从 \(u\)\(t\) 时刻跳 \(2^k\) 步后到达的点,如果中途走到了比 \(t_u\) 大的点就停,这样再记录一个 \(g_{u, t, k}\) 表示实际走的路径。那么查询就很简单了,但是预处理这个倍增数组比较困难,因为每次找下一个我们同样需要进行若干次 \(t_i\) 变大的过程,这样处理的复杂度会变成 \(\log^3\) 之类的东西。

考虑修改一下定义,我们只需要在 \(t_i\) 相等的点之间跳,我们可以只考虑同层之间的倍增数组,这样倍增的过程就很容易了,因为 \(t_i\) 不会发生改变。于是我们可以设 \(f_{u, t, k}\) 表示从 \(u\)\(t\) 时刻跳过 \(2^k\) 个与 \(t_u\) 相等的点后到达的点,或者到达的第一个 \(> t_u\) 的点。我们只需要求出 \(f_{u, t, 0}\),然后倍增的部分就简单了。求 \(f_{u, t, 0}\) 我们只需要先走一步走到一个 \(< t_u\) 的点(如果 \(\ge t_u\) 就直接结束了),然后再利用前面处理的倍增数组跳到下一个 \(\ge t_u\) 的点。这样就可以得到一个总复杂度 \(O(n (\log m + \log V) + q \log m (\log m + \log V))\) 的算法了。

P7565 [JOISC 2021 Day3] ビーバーの会合 2

挺简单的。

考虑合法的点一定形成一条链,也就是说我们要找一条链,然后把点均分到两端的子树。容易发现奇数的时候答案一定是 \(1\),因为把一个点移动到另外一个点的变化一定不等于 \(0\),那么只考虑偶数。

树上路径有两种情况,一种是祖先,一种不是,祖先关系的两棵子树是一个子树加一个子树补,另一种情况就是两棵子树。前者看着很丑,我们找个重心作为根,这样发现任意一个子树都小于等于 \(\frac n2\),子树补都大于等于 \(\frac n2\),那么两棵子树大小的 \(\min\) 就一定取到子树而不是子树补,那其实子树补也不重要了,我们就只考虑两棵子树的情况就行了。我们按照子树大小排序,从大到小依次加入每一个点,然后动态维护一下直径就行了。很简单也很好写的单 \(\log\) 做法,甚至还能优化到线性。

P8425 [JOI Open 2022] 长颈鹿(Giraffes)

首先从比较显然的条件入手,考虑 \(1\)\(n\),发现不可能这两者都不在边界上,否则一定不合法。那么我们可以从开头或结尾删去一个 \(1\) 或一个 \(n\),得到一个新序列。注意到新序列的值域仍然是连续的,于是我们实际上得到了一个子序列,我们又可以继续进行删除最大值或者最小值的操作。那么题目中的条件就是,满足不断删除位于开头或结尾的最大值或最小值,能够将整个序列删除完毕。那么我们就很容易得到一个 \(O(n^3)\) 的 DP 了:设 \(f_{l, r, x}\) 表示删除到 \([l, r]\) 且值域为 \([x, x + (r - l)]\) 时,需要修改的最小个数。每次直接决策最大值最小值在开头还是结尾,然后判断是否需要修改即可。

做到这里看起来就不好优化了,注意到题目中给出的“随机排列”的性质,我们猜测一下答案可能会很大。发现合法的序列一定可以划分成一个上升序列和一个下降序列,如果要尽可能少修改的话,我们就需要尽可能保留原来已经形成的上升序列和下降序列,而这就是排列的 LIS 加 LDS,而众所周知随机排列的 LIS 期望是 \(O(\sqrt{n})\) 级别的,于是实际上最后的答案应当等于 \(n - O(\sqrt{n})\)。于是我们改成求最多能使多少数不变,这样 DP 的值域就是 \(O(\sqrt{n})\) 级别的了。

发现这是状态很大但是值域很小,考虑把一维状态与值域反转过来。但是现在的 DP 并不方便做这件事情,于是我们将问题进行一些转化。

考虑将排列画到坐标轴上,那么每一时刻剩下的数就是这个坐标轴上的一个正方形,注意到这相当于每次将正方形向右上、右下、左上、左下缩减一格,如果对应位置有关键点那么就使答案加 \(1\)。那么我们可以把原来的问题看成:找一个最长的正方形序列,满足后者包含在前者,且每个矩形的四个角处一定有一个关键点,且后者不能与前者的关键点在同一行或同一列。由于我们要调换值域与下标,注意到如果固定正方形的一个角,那么答案关于正方形的边长是单调的,那么 DP 就可以变成:\(f_{i, j, k}\) 表示以 \((i, p_i)\) 为一个角、方向为 \(j\)(四种方向),当前序列长度为 \(k\) 时,边长最小是多少。转移就相当于在 \((i, p_i)\)\(j\) 方向内找到一个序列长度为 \(k-1\) 的正方形,并找到边长的最小值。注意由于边长需要考虑矩形到上边界与到右边界哪个近,我们可以将这个 2-side 矩形拆成两个三角形,这是二维偏序容易解决的问题。实际实现中我们只需要存上一个长度中的所有正方形就行了,并不需要记录 DP 值。而八种情况可以通过坐标翻转实现。

P10221 [省选联考 2024] 重塑时光

突然就理解了一切。

唉,赛时想了一些类似的东西,但是还是想不到这个啊。赛时思路太混乱了,导致确实啥都想不出来。

容易发现题目中的限制相当于排列必须是给定 DAG 的某个拓扑序。考察划分成了若干段,我们看每段里的元素集合。首先每个集合内需要计算拓扑序方案,这可以很简单的 \(O(2^n \mathrm{poly}(n))\) 的求出。然后还需要满足的是,将每个集合缩成一个点后,新形成的图是一张 DAG。假如可以,那么这样的集合划分就是合法的,其任意一种排列都是对的,假设划分成了 \(i\) 段,方案数就是 \(k! \times (k+1)^{\underline{i}}\),前者是划线的顺序,后者是每一段分配到哪两条划线中间。最后除以 \((n+k)!\) 就是答案。

现在问题变成了,有多少集合划分满足缩点后形成 DAG。这相当于对 DAG 图进行计数,根据之前写过的博客,DAG 计数只需要每次枚举一个入度为 \(0\) 的集合,然后乘上容斥系数 \((-1)^{t+1}\),其中 \(t\) 是集合。这里我们需要找到入度为 \(0\) 的集合,这里面先划分成 \(i\) 个集合,并求出每个集合内部拓扑序方案的乘积,这部分直接子集卷积 \(O(3^n n)\) 求出。然后要计算答案,我们需要知道最后一共划分成了多少个点,所以有大致形如 \(g_{S, i} \times (-1)^{i+1} \times f_{T, j} \to f_{T \setminus S, i+j}\),直接转移是 \(O(3^n n^2)\) 的了,这是一个卷积的形式,可以直接写成一个 \(n\) 次多项式乘积,那直接带入 \(n+1\) 个点值计算,最后插值一下就行了,这样复杂度就是 \(O(3^n n)\) 的了。

代码回头再写。

2024.3.20

上午模拟赛 T1 原题呃呃了,冲了一场 T3 冲出来了,还爆了个标,自我感觉良好

T3 是把 CF453E 放到了二维平面上,可以 K-D Tree 将矩形拆成 \(O(\sqrt{n})\) 个连续的区间然后就可以套那题做法 \(O(n \sqrt{n} \log n)\) 了,并不是很优秀,不过可以把颜色覆盖直接放到 K-D Tree 上去做,然后就可以去掉 ODT 的一个 \(\log\),然后查询和的部分是 \(O(n)\) 个点,\(O(n\sqrt{n})\) 次矩形求和,可以分块做到修改 \(O(\sqrt n)\) 查询 \(O(1)\),这样就可以把 \(\log\) 平衡掉了。

然后下午把省选 D2T2 写了,我草这玩意是真好写啊,唉。能做的四道题算是都改完了,感觉这次省选的两天的 T1 T2 确实都是很可做的,终究是能力不足了吧,有一种和去年国赛 D2T2 没想出来的一样的感觉,就是完全像是自己可以做出来的题,想明白之后也能自己写出来,但是场上就是啥都想不到。唉。

CF1943E2 MEX Game 2 (Hard Version)

好像确实不是很难啊,但是确实又是想了半天才想明白,要是我自己打 cf 肯定是过不了的了。

这种一个人要最大一个人要最小的博弈论肯定是二分答案,考虑二分最后结果 \(\ge x\),这意味着 Alice 必须取遍 \([0, x)\) 的所有值,而其它值是不重要的。

此时这几个数之间没有区别了,只关心他们的出现次数,先把前 \(x\) 个数量排下序。考虑 Alice 的决策,容易发现 Alice 肯定每次选数量最小的一个。Bob 看起来不好决策,我们考虑枚举最后 Bob 会将哪个数取完。但是 Bob 取一些数之后可能就无序了,不过只需要钦定 Bob 取完后有序即可,Bob 如果取完后无序那么一定存在方案能够取到排好序后的数列。那么假如 Bob 要取完 \(i\),Bob 肯定每次会尽可能去取位置较大的,使得 \(i\) 数量尽可能少,然后 Alice 每次会删除序列第一个。可以画一个阶梯图,那么 Bob 的取法相当于一行一行取,Alice 相当于一次删除最左边一列。发现过程分两部分,前一部分 Bob 取的值与 Alice 取的值完全无关,然后到某一时刻 Bob 取到了 Alice 将要取的一列,此时发现剩下的形状就一定是一个矩形了,假如总共剩下 \(x\) 个数,还剩下 \(i\) 种数,那么 Bob 每次令 \(x \gets x - k\),Alice 每次令 \(x \gets x - \lfloor\frac xi\rfloor, i \gets i - 1\)。我们可以递推求出对于每一种 \(i\),其最后能够取完的最大的 \(x\) 的值,然后就可以快速判断当前能不能全部取完了。而这个分界点是单调的,直接双指针即可找到这个分界点,这样除去排序外剩下的部分可以 \(O(n)\) 解决。这样总复杂度就是 \(O(n \log n \log k)\)

有点傻逼的地方:好像当时 cf 没有 64 bit 的编译器(洛谷 rmj 目前也没有绑定到 64 bit 的编译器),所以如果计算哪个界可能会需要用 int128,官方题解是对于每一个宽度的矩形记录其最小可能的 \(x\),这样从大往小转移一边看最后能否到 \(0\) 就行了。

2024.3.21

上午看了一上午通用测评号,好像大概推出了题解中的 50 分做法,然后发现这个做法屁用没有,,,然后摆了,不想做这题了

做点平和的题吧

QOJ5569 Distinct Subsequences

首先本质不同子序列有经典 DP:设 \(lst_i\) 表示上一个与 \(s_i\) 相同的位置,那么有 \(f_i = \sum_{j=lst_i}^{i-1} f_j\),即如果上一个数字选在 \(lst_i\) 之前,那么下一个选择的位置应该是 \(lst_i\) 而不是 \(i\)。然后前缀和一下即可得到 \(g_i = g_{i-1} + g_{i-1} - g_{lst_i - 1}\)

考虑加一维表示当前选择了多少数,那么就是 \(g_{i, j} = g_{i - 1, j} + g_{i - 1, j - 1} - g_{lst_i - 1, j - 1}\)。我们要求的就是 \(g_{n, k}\)

这个东西看起来并不好优化转移,所以考虑写成多项式,即 \(G_i(x) = (1+x) G_{i-1}(x) - x G_{lst_i - 1}(x)\)。但是这里涉及到 \(lst_i - 1\),用到的多项式看起来跨度很大。不过注意到值域为 \(2\),那么我们可以直接存上一个 \(0\) 与上一个 \(1\) 的位置的多项式是什么,设 \(pre_{i, 0/1}\) 表示 \([1, i]\) 中最后一次 \(0/1\) 的出现位置,那么 \(lst_i = pre_{i - 1, s_i}\)。我们考虑将三个转移中需要用到的量 \(G_{pre_{i, 0}}, G_{pre_{i, 1}}, G_i\) 看作一个横向量,然后用矩阵刻画转移,容易得到转移矩阵。那么只需要将这 \(n\) 个矩阵分治乘起来就行了。时间复杂度 \(O(3^2 n \log^2 n)\)。我实现好像很慢啊,跑了 5.5s,卡着线跑进去了(

2024.3.22

模拟赛,水平很高。

额反正就那啥了,不管,不重要。

晚上才知道 JOISC 开始了,但是凑不够 5h 了,而且还出去吃饭了,只好随便开了下,晚上想了 1.5h 好像大概口胡出 T1 做法了,感觉应该是对的,不过没空写了 ;-;

明天正经打 JOISC 了

QOJ5568 Cyclic Shifts

想到了第一步,然后不会了,,

考虑选 \(n-1\) 个位置会发生什么,发现等价于将没选的那个位置与前一个位置 swap,然后再整体向右循环移位一次。

只有这个操作没啥用,但是我们可以选择多个不相邻的位置不选,然后就可以同时 swap 多个位置了。而且注意到每次操作的代价不会超过 \(\frac 2n\)

那么我们就可以 sort 了。我们考虑冒泡,不过每次尽可能多 swap,于是先 swap 所有奇数位置再 swap 所有偶数位置即可。可以证明 \(n\) 次以内就能排好序,这个排序方法是与 CF1558F Strange Sort 的做法相同的。然后我们进行 \(n\) 次就可以恰好转一圈转回来了,这样正好 \(2\) 的代价。

2024.3.23

不知道该干啥。

JOISC Day3 啥都不会啊!炸裂了。

JOISC 2024 D2T1 Board Game

比赛结束了应该可以放题解了吧(虽然我也只口胡了

首先考虑策略肯定大致是 \(1\) 棋子走到某个关键点后,然后剩下的棋子依次走到某个关键点上,接下来 \(1\) 棋子每走到一个关键点其他棋子就会走 \(1 \sim 2\) 步结束当前回合。\(1\) 回合是因为可能棋子旁边就有其它关键点。我们称 \(1\) 回合能结束的关键点为第一类关键点,\(2\) 回合结束的点为第二类关键点。

首先如果 \(1\) 棋子不经过任何关键点那么直接找最短路即可,接下来考虑的全部都是至少经过一个关键点的答案,可以把图复制一份来实现强制经过至少一个关键点。那么考虑维护出一个函数 \(f(w)\) 表示经过 \(w\) 个关键点时需要额外走的距离。考虑每一个另外的棋子的答案,然后加起来就是 \(f(w)\)

对于一个棋子来说,它有三种可能性:

  • 第一回合走到一个第二类关键点,这样接下来每一回合都有 \(2\) 的贡献;
  • 第一回合走到一个第一类关键点,这样接下来每一回合都有 \(1\) 的贡献;
  • 前若干个回合依次经过几个第一类关键点,然后最后走到一个第二类关键点,再往后每一回合贡献为 \(2\)

前两者的图像均为第一回合存在一个常数 \(c\),然后接下来是斜率为 \(1/2\) 的直线。第三种情况图像比较奇怪,但是注意到每回合的距离至少为 \(2\),由于最后我们要对所有的函数取 \(\min\),所以前面没有到达第二类关键点的时候都不会成为最小值,于是可以将图像简化为第一回合后斜率为 \(1\) 的直线,所以只需要考虑到任意第一类关键点的距离减去经过的关键点即可。当然直接整负权图不太好,可以将第一类关键点周围的边权从 \(1\) 改成 \(0.5\),效果是一样的。于是,我们一定能够得到一个除第一回合外至多两段的函数,一段斜率为 \(2\),一段斜率为 \(1\)。由于我们已经钦定一定经过一个关键点了,可以把第一回合的答案提前累计上,或者向左平移一位看成截距。总之最后发现 \(f(w)\) 是一个 \(k\) 段的上凸函数,且根据上面的过程我们容易维护出来这个函数。

考虑怎么解决原问题,最朴素的想法就是设 \(g_{u, i}\) 表示从 \(1\)\(u\) 经过 \(i\) 个关键点的最短距离,那么每个点的答案就是 \(\min g_{u, i} + f(i)\)。这样就可以得到 \(O(n^2)\) 的做法了。

显然不够优秀,我们考虑去掉第二维。我们考虑把贡献直接加到关键点上,然后求一遍最短路就行了,但是由于 \(f(i)\) 是个分段函数,没法直接平均的把权值分到关键点上。注意到 \(f(i)\) 可以看作是由 \(k\) 个一次函数取 \(\min\) 得到的,于是我们可以单独对每一个一次函数进行考虑,然后取 \(\min\) 即可。这样我们给每一个关键点上加一个点权,再跑一边最短路即可。直接跑 Dijkstra 复杂度就是 \(O((n+m)k \log m)\),注意到这是一个只有两种边权的图,我们可以用两个队列来做 BFS 就可以了,复杂度 \(O((n+m)k)\),可以拿到 68 分。

如果 \(k\) 很大这个做法就寄了。但是注意到一件事,就是当 \(k\) 很大的时候,关键点的额外花费也大了很多,每多走一个关键点额外花费至少为 \(k\)。对于每一个点,我们考虑到达它需要经过的最少的关键点 \(w_u\),这时候的答案上界是 \(f(w_u) + n\)。如果经过更多的关键点,\(g_{u, i}\) 最多也无法减少超过 \(n\),于是如果 \(f(w_u)\) 增加了超过 \(n\) 就一定不可能成为更优的答案了,于是实际上到每个点最短路所经过的关键点只有可能在 \([w_u, w_u + \frac nk]\) 这个范围内,那么我们直接暴力做最短路即可得到一个 \(O(\frac{(n+m) n}{k})\) 的做法了。两部分平衡一下,即可得到 \(O(n \sqrt n)\) 的做法(假设 \(n, m\) 同阶)。

感觉很有趣啊,一开始以为会用到整点凸包点数的性质,然后发现 \(f(w)\) 段数最多 \(O(k)\) 的,然后推了下发现 \(g_{u, i}\) 只有下凸包的点有用,这个凸包点数不超过 \(O(n^\frac 23)\),但是好像没啥用,也没法合并。然后意识到 \(k\) 大的时候好像增长很快,然后发现可以平衡。

2024.3.25

usaco 三个紫,还有两个是下位紫,我只会一个,,,

感觉没救了

写个模拟赛题的题解

硬币序列

给定一个长度为 \(n\)\(01\) 串,有些位置是问号,你需要将问号替换成 \(01\) 使得最长连续相同子段最短,且 \(0\) 的个数为 \(a\)\(1\) 的个数为 \(b\)。构造方案。\(\sum n \le 10^6\)

先二分答案一波。首先我们只需要考虑 \(0\) 的个数,恰好等于 \(a\) 不好考虑,我们可以尝试找出 \(0\) 的个数的最大值与最小值,然后猜测假如 \(a\) 在这个区间内就存在方案。

这可以通过 DP 实现,设 \(f_{i, 0/1}\) 表示考虑到 \(i\),且最后一段为 \(0/1\),转移是在一个区间内取 \(\min\)\(\max\),单调队列实现即可。

然后考虑怎么证明存在,以及如何构造方案。我们先构造出最小和最大的两种方案,我们通过这两种方案得到一个中间的方案。考虑将最大值的一个前缀与最小值的一个后缀拼接起来,这样分界点每移动一位 \(0\) 的个数变化量为 \(1\),显然能取遍 \([\min, \max]\) 中的所有值。但是拼接前后缀并不一定满足条件,因为有可能前缀最后一个位置与后缀第一个位置相等,使得拼接起来的段更长了,此时我们可以将这种相等的部分直接跳过。考虑极长的一段相等的部分,直接跳过这一整段,发现此时变化量仍然是 \(1\),于是说明一定还是有解的。

打怪升级

给定两个长度为 \(n\) 的数列 \(a,b\),一次操作可以将 \(a\) 中两个不同位置加一,求有多少种操作方式将 \(a\) 变成 \(b\),且操作过程中 \(a\) 中的元素两两不同。\(n\le 30, a_i, b_i \le 200\)

发现这东西是一个类似于求不交 \(n\) 条路径方案数的东西,考虑类似于 LGV 的求解方法,枚举一个 \(b\) 的排列,此时可以将操作过程中 \(a\) 的元素两两不同的限制给去掉。

问题变成了有一个新的序列 \(c_i = b_i - a_i\),每次选择两个位置减 \(1\),问有多少减成 \(0\) 的方案。考虑操作序列,相当于限制操作序列上每两个数不同,考虑容斥,钦定 \(k\) 个位置相同,记 \(\sum c_i = s\),那么答案就是:

\[\sum_{k=0}^{s/2} (-1)^k \binom{s/2}{k} \sum_{2d_i \le c_i, \sum d_i = k} \binom{k}{d_1, d_2, \cdots, d_n} \binom{s-2k}{c_1 - 2d_1, c_2 - 2 d_2, \cdots, c_n - 2d_n} \]

后面是在考虑钦定相等的位置中每个数有多少。拆一下即可得到:

\[\sum_{k=0}^{s/2} (-1)^k \binom{s/2}{k} k! (s-2k)! \sum_{2d_i \le c_i, \sum d_i = k} \frac{1}{\prod_{i=1}^n d_i!(c_i - 2d_i)!} \]

注意到对于任意一种 \(b\) 的排列 \(s\) 是不变的,那么前面的系数对于所有排列来说就也是不变的,我们只需要对所有排列和每个 \(k\) 求出后面这个东西的和即可。后面这个东西显然是若干个多项式的乘积,记 \(F_c(x) = \sum_{i=0}^{c/2} \frac{x^i}{i!(c-2i)!}\),那么后面这个东西就是 \([x^k] \prod_{i=1}^n F_{c_i}(x)\)

发现此时每条路径的贡献相互独立了,所以可以直接上 LGV 了,求一个多项式行列式即可。直接插值即可。

2024.3.26

把 USACO 题补完了,确实好简单,,,一个题都没看题解

唉我真的感觉我现在脑子很有问题 就是完全是自己能做的题然后就是死活不会 然后突然就意识到我是傻逼

P10283 [USACO24OPEN] Identity Theft P

赛时做法,我做法比较麻烦啊。

考虑建 Trie,然后现在问题就是 Trie 上有若干的节点上有棋子,每次将棋子移动到一个空子树或一个叶子上去。空子树或者叶子一定是填成满二叉树,推一下贡献发现操作次数关于点数是一个凸函数,意味着我们可以贪心。

现在问题就是每次找到一个棋子,使得棋子到子树内一个空子树或叶子的距离加其贡献最小。考察对于一个空子树或者叶子来说较深的点一定先被选,而又因为所有棋子深度之和是 \(O(\sum |S_i|)\) 的,所以可以每次暴力更新这个点到根的所有点的子树内贡献最小值,线段树搞一下,然后拿堆维护下每个节点上的最小值即可。复杂度 \(O(\sum S_i \log n)\)

P10284 [USACO24OPEN] Splitting Haybales P

不懂怎么利用递减的性质做。

考虑一个暴力的做法,分块然后每次快速维护出来一个区间内的函数复合。

然后直接套取 ARC149D 的做法就行了。就是注意到这个函数关于 \(0.5\) 是对称的,于是如果有一个点小于等于 \(0\) 那么就可以给他翻转到一个大于 \(0\) 的函数值上去,这样我们只用维护定义域为正的函数。每次将值域平移,然后注意到此时可能有负有正,我们将较小的一边对应到较大的一边上,然后只维护较大的一边,这样均摊下来总复杂度就是 \(O(V)\) 的,分块即可得到复杂度 \(O((V+q)\sqrt{n})\)

P10285 [USACO24OPEN] Activating Robots P

注意到每次放完一个机器人后当前位置一定与机器人重合,而机器人的位置可以简单的通过经过的时间计算出来,考虑设 \(f_{S, i}\) 表示已经放了 \(S\) 集合的机器人,当前在第 \(i\) 个机器人的位置,所需的最少时间,然后枚举下一个放的机器人,找到与下一个机器人相遇的最短时间,然后二分找到下一个能到达的关键点即可。每次二分会带 \(\log\),注意到本质不同的起点终点对只有 \(O(nr)\) 个,因为起点一定在某个关键点上,对这些对都二分一遍即可,或者记忆化一下。复杂度 \(O(2^r r^2 + nr \log n)\)

QOJ5573 Holiday Regifting

不是很会这种题啊 😕 做法挺简单的,但是感觉可能没见过类似的东西确实想不到怎么做

考虑按照拓扑序依次加入每一个点,维护使得前 \(i\) 个点全为 \(0\) 的周期 \(ans\)。显然每一个前缀都是周期性的。同时,我们维护出当前最小周期的时候所有点上各有多少礼物,设这个值为 \(a_i\)

考虑加入下一个点,一个周期会得到 \(a_i\) 个礼物,要想礼物全部没掉就需要 \(\frac{a_i}{\gcd(ans, a_i)}\) 个周期。我们将答案乘上这个值,并且将所有点上的礼物乘以这个值,然后再依据题意模拟一遍,就能得到新的最小周期与剩下所有点上礼物的个数了。时间复杂度 \(O(nm)\)

posted @ 2024-03-19 09:52  APJifengc  阅读(542)  评论(1编辑  收藏  举报