八月杂记
板刷 CF
ds
CF1899G *1900
想到的:考虑先求出 dfs 序,然后用主席树去维护排列 \(p\),每次查询就相当于问 \(l\sim r\) 中有没有节点的 dfs 序在 \(dfn_u\sim dfn_u+sz_u-1\) 中。
没想到的:主席树做法无,可以树状数组离线、dsu on tree 做。
分治
CF1917C *1600
想到的:若 \(a\) 全为 \(0\),那最优策略必为每次操作一后跟操作二,所以只需要判断在第一次进行操作二之前能获得的最大值是多少即可。同时 \(n\le 2000\),所以可以 \(n^2\)?
没想到的:在 \(a\) 数组不全为 \(0\) 的情况下,最开始最多有 \(n\) 的贡献。一开始直接进行操作二,然后一直进行最优策略。在总共操作了 \(2\times n + 1\) 次后就可以得到 \(n\) 的贡献。所以在 \(d<2\times n\) 时直接暴力,否则因为在 \(2\times n\) 次操作后必然可以得到 \(n\) 的贡献,所以枚举到 \(2\times n\) 即可。
构造
CF1899F *1600
想到的:直接构造一条链,然后如果 \(d = n-1\) 就输出 -1 -1 -1
,否则将 \(n\) 与 \(n-1\) 号节点之间的边断开,然后将 \(n\) 与 \(d\) 相连。
没想到的:题目的操作会延续到后面(但没有很大的影响)。
思维
CF1709D *1700
想到的:对于每次询问,如果纵坐标相减的绝对值不是 \(k\) 的倍数就输出 NO
,否则用 st
表查询区间 \(a\) 数组中的最大值,判断绕过最大值需要往上走多少步,如果超出了 \(n\) 就输出 NO
,否则判断在走完之后和终点横坐标的差的绝对值是否是 \(k\) 的倍数。是则输出 YES
,否则输出 YES
。要特判区间最大值小于横坐标的情况。
过题过程:第一发因为没特判 WA 了。
没想到的:无。
CF2085C *1600
想到的:
11
01
或
11
10
这样的显然可以通过加的操作完成,于是直接模拟?细节太多,还有两种情况。
没想到的:容易发现,如果 \(2^k\oplus x,x\in \N\),有 \(2^k\oplus x=2^k+x\),所以把 \(x\)、\(y\) 中较大的那个数改成 \(2^k\) 即可。注意到当 \(x=y\) 时是显然不行的。
CF1851F *1800
没想到的:《观察·注意·启示》。
CF1851G *2000
没想到的:要浅推式子啊。如果路线如此 \(i\rightarrow j\rightarrow k\) 要满足 \(h_j-h_i\le e\)、\(h_k-h_j\le e - h_j+h_i\),所以 \(h_k\le e + h_i\) 所以路径上的每一个点都要满足这个条件。此时我们可以对询问离线并按 \(h_u+e\) 从小到大排序,然后将每条边的边权视作 \(\max(h_u, h_v)\),然后双指针加边即可。(这种套路很板啊)
CF2131G *2000
想到的:发现在操作时会有很多重复的操作,也就是说有重叠子问题,考虑 dp。接着手玩一下发现:
- 要把 \(\{2\}\) 变为空需要 \(2\) 次操作。
- 要把 \(\{3\}\) 变为空需要 \(4\) 次操作。
- 要把 \(\{4\}\) 变为空需要 \(8\) 次操作。
这也就意味着如果有一个 \(s_i\ge 31\) 那就一定取不完了。有用吗?所以可以按照这样一个方式预处理出来要消掉每一个数所需要的花费,然后模拟?Coding……无聊的题……
没想到的:无。
dp
CF1841C *1800
想到的:考虑倒序枚举然后 dp,可以直接暴力枚举每个位置会改成什么字符。但这样却不能判断后缀最大值?那把状态改成三维 \(f_{i,0/1,k}\) 表示处理完后 \(i\) 位,\(1\) 表示已经修改;\(0\) 反之,后缀最大值为 \(k\) 的答案。转移显然。
没想到的:要初始化为极小值……。
CF1841D *2000
想到的:dp。
没想到的:对于线段覆盖问题,考虑按右端点排序。要大胆想 dp,不要畏难。
CF2086D *1700
想到的:将 \(\%2=0\) 和 \(\%2=1\) 的分开考虑,然后问题就转变成背包,最后好像还要用组合数算一下剩下的方案数。这东西显然可以先排列一下,然后再去重,即 \(ans = \frac{len1!\times len2!}{\prod c_i!}\)。接下来直接乘上背包的答案即可。
CF2005C *1800
想到的:\(n^2\) 枚举进行 dp?\(f_{i,j}\) 表示前 \(i\) 个串以及以 \(j\) 结尾的最大差值。但实际上可以把 \(i\) 给压掉,因为我们只关心前面的最大 dp 值。
没想到的:dp 的一些细节,比如初始化要和 \(0\) 比较。
CF2053E *1900
想到的:这不显然往最近的叶子节点走吗?从叶子节点 bfs dp 预处理每个点到叶子节点的最短距离,然后排个序,对于每个点到叶子节点的距离,找有多少个严格大于它的点即可。\kk,看错题了,没有那么简单。因为毛毛虫的长度是不变的,所以答案中必然先会有叶子节点的个数乘上非叶子节点的个数,然后就是在先手走了一步后后手刚好到一个叶子节点的旁边,然后这样就也能获胜,否则平局。这显然是一个实现较为麻烦的树形 dp 了。
考虑定义 \(f_u\) 表示以 \(u\) 为根的子树内在往上走一步后会到达叶子节点旁边的点的个数。则其转移为:
\(distance_u\) 表示点 \(u\) 到最近的叶节点的距离。
再定义 \(dp_u\) 表示以 \(u\) 为根的子树内距离叶子节点的距离为大于 \(1\) 的点的个数,转移显然。
考虑定义 \(g_u\) 表示当点 \(u\) 为子树的根时的答案数量。则其转移为:
最终答案即为:\(g_{root}\)。
没想到的:原来有简便的换根写法。
CF2060F *2200
想到的:首先注意到一些性质,一个数的因数是很少的,所以 \(a\) 数组中必然有很多 \(1\)。同时,这个数据范围是可以 \(O(k\sqrt{k})\) 过的,所以可以直接对每个 \(x\) 暴力枚举因数。因为有很多重叠子问题,所以考虑 dp。注意到只有在 \(n\le 17\) 才会对组成有影响,所以可以在定义状态为:\(f_{x,i}, i\le \min(17, n)\) 表示在最短数组长度为 \(i\) 时,乘积为 \(x\) 的方案数。这样子就需要预处理一下因数了。What about transfer?
还没完,这个方程显然没有去重。不会了,啊啊啊!!!甲烷了……可以先考虑不包括 \(1\) 的情况,然后式子就有了。
没想到的:原来有上指标求和这种东西……\(\sum_{k=r}^{n}C^r_k=C_{n+1}^{r+1}\)。
贪心
CF2085D *2000
看了 tag:贪心,堆。
想到的:是反悔贪心?哦,吃了几盘寿司是可以被算出来的;不行,因为有可能会有不操作的情况。那就用一个变量表示现在还有多少空闲的天,如果还够吃就把寿司 push
小根堆,否则 pop
并且空闲的天增加。这还是不能保证一定有足够的天来吃啊!
没想到的:因为后面的天数会影响现在的取法,所以考虑从后往前贪心。
CF1996F *1900
想到的:好像只能贪心了啊?怎么转化呢,需要发现一些性质。考虑 \(a_i\) 和 \(a_i-b_i\) 发生了什么转变,即 \(a_i\) 在原数组中的大小位置变化了。而最朴素贪心的方法便是用大根堆去维护原数组中的元素的最大值,然后每次模拟操作。那我们是否可以对于每个 \(a_i\) 都计算出它会对答案有多少次贡献呢?可以使用二分来算出每个数在进行多少次操作后会变成最大值、变成不是最大值。
没想到的:竟然是二分答案……假设我们有一个最大值 \(x\),在每次操作时我们一定不会取 \(a_i< x\) 的 \(a_i\),此时我们就可以对 \(x\) 进行二分,找到第一个使得把所有 \(a_i\ge x\) 的 \(a_i\) 取完后所用的次数 \(\ge k\) 的 \(x\),然后统计答案。
算法学习
矩阵快速幂
简而言之,两个矩阵只能在列和行数相等时进行乘法,所以不满足交换律。然后快速幂就没什么区别了,唯一在于重载运算符。
一般的题目都是需要快速递推答案时用到矩阵快速幂,所以推出初始矩阵是最难的,这需要大量练习。
Lucas 定理
结论:\(C^m_n\bmod p = C^{m\div p}_{n\div p}\times C^{m\bmod p}_{n\bmod p}\)。
Master Theorem 主定理
对于形如 \(T(n)=aT(\lceil \frac{n}{b}\rceil)+O(n^k)\) 的式子的时间复杂度分析,有:
- \(\log_b a > n^k\),时间复杂度为:\(O(n^{\log_b a})\)。
- \(\log_b a = n^k\),时间复杂度为:\(O(n^k\log n)\)。
- \(\log_b a < n^k\),时间复杂度为:\(O(n^k)\)。