做题笔记
只记录简单做法,为防止长度过长加载速度慢,所以就不贴代码了,反正到处都存了。
从 11.16 号开始的笔记都会标上日期并按照背景分类。
1.CF1059E Split the Tree
简单 2400 的题,做题时比较浮躁,静不下来,做了比较久。
考虑到每个点必然包含在一条垂直路径,根节点必然是垂直路径的顶端。
定义 f[i] 表示以 i 号点为垂直路径顶端点,i 的子树内的节点全覆盖的最少垂直路径数量。
显然满足
\(\sum_{v \in son_x} f_v \le f_x \le \sum_{v \in son_x} f_v\) + 1 即要么单飞,要么与下面某条路径合并
转移即判断能否找到一个单飞转移点与当前点形成路径满足条件。
由此可知,单飞的越晚越优,即能不单飞就不单飞,既把单飞机会留给之后使转移更多,又让当前位置答案更小
由此发现,当答案最优时,单飞与否的状态也是不劣的
明显,当确定一个点单飞后,做一个差分维护是否单飞
(upd:10.21 我不用管他是跟哪个儿子一起飞的,我只用知道它是否有可能能飞就好了~
可以倍增解决。
复杂度 \(O(n \log n)\)
2.[清北学堂] 校门外的树
研究标程把这道题弄明白了,于是来写一篇题解。
题意:
询问一个区间内的元素的最小差值。
分析本题,发现能够对答案产生贡献的数对具有 \(n^2\) 个,但是其中存在大量的无用的数对状态。
举个例子,对于点 \(i,j\) ,满足 \(h_i < h_j\) ,则发现满足 \(k > j , h_j < h_k\) 的 \(k\) ,都是无法与 \(i\) 对任何一个区间的答案产生贡献的。因为他离 \(i\) 比 \(j\) 远,又没有 \(j\) 贡献答案优秀。
类似地,我们考虑能否进一步减少我们的有用的数对数量。
考虑 \(k>j , h_k < h_j\) ,发现这样的一些 \(k\) 并不完全可能与 \(i\) 产生贡献,因为这些数有可能与 \(j\) 组成数对会答案产生更优秀的贡献。
简单分析可以发现,当 \(h_k < (h_j + h_i) / 2\) 时,\(i,k\) 才不一定劣。
按照上述分析不断迭代下去,容易发现,对于每个 \(i\) ,最只有 \(\log M\) 个(\(M\) 为值域) \(j\) 满足 \(j > i\) 且与 \(i\) 组成数对不一定劣。
同理对 \(j > i , h_j < h_i\) 的 \(j\) 寻找不一定劣的数对。
因此有用的数对个数就被压缩到了 \(\log M\)(\(M\) 为值域) 对。
考虑寻找这些数对。只用开一棵动态开点线段树从后往前扫一遍,每次寻找满足不一定劣值域范围的最近的位置就好了。
找到数对后,离线询问,左端点从大到小排序扫一遍,求符合范围的数对权值最小值,用树状数组维护一下就好了。
3.CF1451F Nullify The Matrix
显然这是一场会结束的游戏,因为路径总是从左上角到右下角,而开头总会减去正整数
考虑使用 SG 函数来分析
考虑类比 Nim 游戏,按照一定规则分类,将异或值做权。
若所有类的值不全为 0 且先手总是可以将所有类变为 0 则先手必胜。
对于初格,只能减,则只要找到包含该类二进制最高位元素即可
对于非初格,任意元素均可实现
发现按左斜线分类恰能满足
4.CF1451E Bitwise Queries
询问 n - 1 次,我们可以知道任意两个元素之间的异或值
通过知道任意两个数的 & 值,我们就可以求出他们的 | 值
((\(x_1\) | \(x_2\)) & (\(x_2\) | \(x_3\))) ^ (\(x_1\) & \(x_3\)) ^ (\(x_1\) & \(x_2\) & \(x_3\)) = \(x_2\)
n + 2 次
E1 solved
显然,要么 a 是排列 , 要么存在两个元素相同
若存在两个元素相同,& 运算求得,然后异或带回即可。
若 a 是排列 , 则必然有 x_1 ^ x_i = n - 1 的位置 , 任选一个 x_j
\(x_2\) = (\(x_1\) | \(x_2\)) & (\(x_2\) | \(x_3\))
E2 solved
5.CF1476F Lanterns
dp , 考虑这样一个转化 , 钦定第一个没被照亮的灯后面都不会被照亮。
扫了一眼题解,说是把可行性转化为最优性。
那么定义 \(f_i\) 表示决策前 \(i\) 个点 , 最多能够覆盖多长的前缀
若 \(f_i > i\) , 则 \(i\) + 1 号灯往右照更优
若 \(f_i <= i\) , 则 \(i\) + 1 号灯可以继续往前照 , 等待未来往回照
也可以往回照 , 二分查找转移点(f数组具有单调性) , RMQ 转移。
总结一下 , 此题难点有且仅有将可行性转化为最优性进行 dp 。
类似的 , 市面上同样存在将可行性转化为计算方案数的题目 , 如:
CF1043F Make It One
这就是一道将可行性转化为方案数以方便使用容斥计算方案是否存在的题目。
但并非所有的可行性 dp 问题都需要转化 , 如:
CF1539E Game with Cards
这就是一道通过简化状态 , 以及大量预处理和数据结构来降低复杂度的题目。
对比发现 , dp 的状态是核心 , 在对 dp 优化时应该优先考虑状态。
或许可以将所有 dp 视为可行性 dp , 而 dp 里面存储的值需要满足无后效性的特点。
存在与否显然满足无后效性。而本题中,最多能覆盖多长的前缀,在第一个转化下具备了无后效性。
那么状态的信息更为丰富之后,转移起来自然更为简单。
所以在 dp 时 , 不妨将一些无后效性的信息全部塞进 dp 的内容中。
大概就和 CSP2019 划分 类似。
6.CF1526E Oolimry and Suffix Array
大概是这份笔记目前最简单的一道了
若 \(s_i\) < \(s_j\) 且 \(a_i\) == \(a_j\) 当且仅当 \(s_{i + 1}\) < \(s_{j + 1}\) (\(s_i\) 指 \(i\) 号元素的排名)。
只用确定使用了多少种字符
按 rk 的顺序遍历一遍 , 可判断出相邻两种是 < 还是 <= 的关系。
枚举有几种字符 , 组合数算一下就好了。
7.CF1325F Ehab's Last Theorem
考虑建 dfs 树
一个点只会往下连 sq - 1 条边 , 否则就会形成大小为 sq 的环
否则随便选点建独立集 , 则每次最多往上排除 sq 个点 , 至少排除 sq 次 , 所以必然有大小为 sq 的独立集。
很有意思的一道题
8.CF1548C The Three Little Pigs
考场读错题了导致一直没做出来。
对于每个询问,相当于是求:
发现求取的组合数不连续,考虑通过处理另两种式子使得求取范围连续。
则定义
考虑利用组合数的恒等式推导方程。
对于 \(j > 0\) 的情况。
对于 \(j = 0\) 的情况考虑利用其他组合数恒等式求解。
首先让我们的组合数连续起来,发现有恒等式,于是可以推出方程。
然后每个询问答案就是 \(f_{x,0}\)
最后说明一下其中的一个恒等式(好像不是特别常见):
考虑组合意义:
相当于本题弱化版,\(n\) 只猪,选一个时刻吃 \(x\) 头猪的方案数,考虑多吃一头猪,并钦定最后一头猪表示时间,为此我们需要在真正的最后一头猪后面加一头猪来表示最后时刻。
这个恒等式应该在看见这道题挖掘性质时被分析出来,不是很难。
9.二叉搜索树
首先考虑 \(n \log n\) 建树。
发现当我们插入完若干个节点过后,发现对于某些左儿子或右儿子为空的点,能够插入进去的点的范围恰好是一个连续的值域区间,并且当有点插入进去之后整个区间会一分为二,并且深度值 \(+1\) 。发现这些区间的端点必然是已经插入进去的值,最初的区间是 \([1,n]\) ,那么我们用 \(set\) 维护插入的点,那么相邻两点之间的区间就是我们维护的连续的值域区间。为了计算贡献,我们还要维护区间的深度权值,在区间的左端点维护一下就好了。
通过枚举全排列,我们可以得到一份正经的 \(30pts\) 的暴力。
考虑正解,发现对于 \([1,l-1]\) 的点完成插入过后,我们可以得到许多值域区间,同时 \([l,r]\) 的区间里的元素恰好会包含于一个值域区间,由于每个值域区间对答案的影响是相互独立的,因此,我们可以对于每个区间单独考虑。
对于每个值域区间内,属于其中的 \([l,r]\) 的元素的插入顺序是不确定的,且是会对后面产生影响的,但我们发现可以利用费用提前计算的小技巧将问题规模划分为子问题。
具体来说就是假设这个值域区间为 \((L,R)\) ,而属于其中的 \([l,r]\) 的元素从小到大排序依次为 \(c_1,c_2,\dots,c_k\) ,那么假设第一个插入的点为 \(c_i\) ,则会带来 \(R - L - 1\) 的深度贡献,同时问题被拆分为往 \((L , c_i)\) 的值域区间内插入点 \(c_1,c_2,\dots,c_{i-1}\) 和 \((c_i , R)\) 的值域区间内插入点 \(c_{i+1},c_{i+2},\dots,c_{k}\) 。(考场想到这个位置就没有推进了,感觉好亏,也是对区间 \(dp\) 极为的不熟悉)接下来就可以直接通过区间 \(dp\) 计算这 \(k\) 个点的插入顺序。
p.s 花花在看 wxk 代码的时候发现了不太一样的做法,大概是将 \(a_i\) 的值作为下标,将下标作为权值建笛卡尔树,实现 \(O(n)\) 建树(非常魔幻)。后续不知道她是怎么处理的,有兴趣的同学可以研究一下。
10.CF1555F Good Graph
简单观察可得,好图是一个简单环权值为 \(1\) 的边仙人掌。
树显然是好图,那么我们把所有边当中的树边拿出来必选。
我们用一个点来表示它连向它父亲的边,同时用树状数组维护每个点到根节点的选中的边的个数。
连环标记边时暴力爬 LCA 修改即可,因为每个边至多出现在一个环当中,只会被修改一次。
11. 「PMOI-4-C」可怜的团主
挺厉害的构造题。
考虑建一棵 dfs 树,叶子节点之间显然无边相连,若叶子节点个数大于等于 \(\lfloor \frac{n}{3} \rfloor\) ,则直接输出独立集。
否则假设有 \(x\) 个叶子节点,我们考虑两两连路径,使得路径覆盖所有点,我们尝试着去构造一下。
发现与道路铺设蛮像,不妨借助道路铺设的思路。
记录每个点需要往上连多少个叶子,显然,除根节点以外,所有点至少保留 \(1\) 个往上连的叶子。
在满足上述前提下,我们把不同子节点传上来的叶子两两相连(优先连传上来两个叶子的点),最后会剩下一到二个点,往上传即可。
到根节点后,直接把根节点与传上来的两个点连路径。
构造完成,可以证明是不会超过限制的。
12. 「PMOI-4-E」排列变换
D 太屑了,不写了。
考虑 \(f(a)\) 的一些性质,发现相同的值是连续一段的。
那么考虑计算断点个数,一个断点显然会带来一的贡献,同时每个排列自带一的贡献。
考虑断点如何形成。假设断点左边值为 \(x\) ,断点右边值为 \(y\)。
若 \(x > y\) ,则说明存在一个值使得它大于它右边的 \(k\) 个数。
反之则存在一个值使得它大于它左边的 \(k\) 个数。
考虑枚举这样的数,并把它的左/右边安排上 \(k\) 个比它小的数,然后算有这样的段的排列个数即可计算答案。
13. CF1521D Nastia Plays with a Tree
考虑从下到上决策,若从下到上,它有 \(x\) 个儿子可断可不断。
若 \(x \le 1\) ,则它与它的父亲可断可不断。
否则,父亲断了显然更优,因为你断的边数确定,断掉父亲可以让未来决策中的 \(x\) 变小,于是操作数会不劣。
对于 \(x > 2\) 的部分,多出来的断了就是,稍微递归处理一下就好了。
14. CF1550F Jumping Around
考虑 \(dp\) ,显然满足单调性,于是每次选取答案最小的一个状态进行转移以保证正确性。
考虑状态,发现你跳完一步之后可以修改灵活度以跳到该跳的点,而该跳的点可以继续转移灵活度。
于是定义 \(dp_{x,k}\) 表示在第 \(x\) 个点,能往左/右转移的当前答案和当前花费灵活度。
因为当一个点完成转移后需要删除,因此转移时用 \(set\) 维护一下未被删除点就好了。
15. CF1554E You
每条边要么是一边先删,要么是另一边先,所以总方案数为 \(2^n\)。
从下到上考虑,发现每个点位置能够决策的只有与父亲的边的关系,因此度数最多改变 \(1\) ,因此对于 \(x | gcd\) 的顺序选择存在唯一方案。
扫一遍,容斥一下就好了。
16. CF1540B Tree Array
观察数据范围,很容易想到去枚举两个点之间的相对顺序关系。
也很容易发现,能对他们之间谁先谁后的概率产生影响的是他们到 lca 之间的点。
这道题最值得记录的点就是如何计算概率的问题。
一般而言我们发现它的概率是不断变化的,感觉不好分析。但是发现往任意一个点走的方向的概率是一样的,因此只需要预处理一下就可以直接算出答案。
17. CF1444C Team-Building
不是很困难,但因为没有想清楚调了很久。
考虑对每个组组内判一下二分图,进行二分图染色,然后答案为所有组的组合减去不合法的总数。
由于组间边是有限的,因此可以直接枚举组间边(没有组间边的肯定是二分图)。
注意到每个组内的点不保证联通,而加上若干条边之后会使得其联通,因此考虑使用并查集判断。
如果一条组间边相连的两个点处于同一个联通块,则判断一下他们的颜色是否相同,否则则合并两个联通块,并在必要时将一个联通块里的所有颜色取反。
将联通块里的所有颜色取反的操作可以用启发式合并实现。
然后就做完了。
18. CF1408E Avoid Rainbow Cycles
考虑如何描述结束后我们构建出来的图,由于在同一集合中的点两两之间都有边,因此考虑建一个虚点来描述这样一件事。
则图中有每条边颜色不同的环,当且仅当在我们的新图中有环。因此考虑选一些边使之无环,容易想到最小生成树,直接算一下就好了。
19. CF1408F Two Different
挺不错的构造题。
一眼下去很容易想到分治,但仔细考虑发现只有当 \(n\) 为 \(2\) 的幂次的时候我们才能够通过分治及合并将所有元素变为一样的。
那么考虑二进制拆分,把 \(2\) 的幂次拆出来分段计算。
有了之前分治的经验,我们发现当两段的元素个数相同,且内部相等时可以把他们合并为一样的。
因此根据二进制的特殊性,从小到大考虑不断合并,当无法把小的合并成大的时我们从最大的一个幂次中取一段出来合并为更大的一个幂次。
这样最后就只会剩下两段。
20. CF1408G Clusterization Counting
很厉害,也很值得去做的一道题。
先考虑哪些组可以存在,不断从小到大加边,当加边之后形成的一个联通块变成一个团了之后就说明该边集可以单独作为一个组。容易发现这些组形成了树形结构。
选了一个作为组之后,其所从属的所有组都不能选,做一遍背包即可。
21. CF1406E Deleting Numbers
值得一做的交互题,融合了大量 trick ,但如果掌握分析技巧也可以行云流水的解决。
数据范围内,质数个数大概有 9700 个左右。 \(\sqrt{n}\) 的范围内质数有 \(66\) 个。
因此对于前 \(66\) 个质数暴力判断个数,对于更大的质数它总共最多只会出现一个。
考虑对之后的质数范围内分块,寻找我们需要的质数在哪个块内,然后对块内暴力查询是哪一个,块的大小取 100 可以通过本题。
竞赛图相关
缩点之后是条链大概是个非常常用的性质。
1. CF1514E Baby Ehab's Hyper Apartment
根据 众所周知 的性质,我们可以很自然的想到把缩点之后的这条链找到。
考虑这条链的性质,发现前面的强连通分量往后面的强连通分量有连边,后面的没有往前面的连边。
则容易发现,一个强连通分量必然是一条哈密顿路上的连续的一段点,考虑求到一条哈密顿路。
考虑递归求解,考虑已经求得两端路径,考虑合并为一条。先考虑两条路的开头,判断单边方向即可判断谁在前面,以保证无论下一步放接下来两个点的任意一个都会有边。
于是把 1 询问当作归并排序比较函数即可。
得到这条路之后就很容易可以通过 2 操作合并点集。
我感觉第一篇题解的二分求哈密顿路是错的。
2021.11.16
2. CF1556F - Sports Betting
了解到了竞赛图缩点后是链的结论这道题就会很容易。
考虑继续维护一条链,答案就是链头的强连通分量的大小。
考虑维护 S 集合组成的链,组成的链头长度为 l 的概率,枚举一个点集组成强连通分量,然后计算插入到链头的概率即可。
考虑计算一个点集形成强连通分量的概率,考虑容斥计算,枚举一个点集作为链头强连通分量,其余随便的概率,总概率减去即可。
2021.11.16
超立方体相关
完全不会系列
1. CF1572D Bridge Club
由超立方体图性质得,显然是二分图,则有 \(n \times 2^n\) 条边。
选择一条过后,会导致有最多只有 $2 \times (n - 1) $ 条边不能再选。
因此任意 \(2 \times k \times (n - 1)\) 条边一定可以选出含有 \(k\) 条边的匹配。
选最大的跑费用流就可。
2021.11.16
2. CF1543E The Final Pursuit
奇怪的构造,但我仍然不会。
构造一组置换非常容易,直接分层图 bfs 就好了。
考虑染色。
考虑解的存在性,发现一个点的颜色为 \(x\) ,则它会作为排列的一部分 \(n\) 次,总共有 \(2^n\) 个点,则 \(x\) 颜色会总共作为排列 \(2^n\) 次。
则只有 \(n | 2^n\) 时才可能有解。
考虑构造。
令 \(n = 2^k\) ,则有性质:\({y \in [0,2^k) | x ^ y}\) 是关于 \(n\) 的排列的集合。
因此考虑将与 \(x\) 相邻的点 \(v\) 的颜色染成 \(c_x ^ y , y \in [0,2^k)\),发现这样构造满足可逆性,因此是正确的。
2021.11.17