8.5~8.16 做题笔记
记录了做到的一些不错的题,也有模板和知识点。
CF1548C
题意
个询问,每次给定 ,求 。。
做法一
设 ,则有
递推就能做了。
用到了上指标求和公式(好像叫什么 Hockey Stick Identity(?):
做法二
设
把 替换成 就得到递推式
比方法一的简洁些。
斐波那契数列通项公式
其中 。
可以扩域做,即把 看作广义的“复数”进行运算。
Codeforces Round #737 (Div. 2)
赛时比较菜,就切了 ABC,D 就是线段树优化 dp,当时有了思路,但没时间写了。E 还是挺不错的,但据说交互库水了点。
长链剖分
树上 k 级祖先
各种做法花里胡哨,这里说下长剖的做法( )。
预处理:
-
对树进行长链剖分,记录每个点所在链的顶点和深度
-
树上倍增求出每个点的 级祖先
-
对于每条链,如果其长度为 ,那么在顶点处记录顶点向上的 个祖先和向下的 个链上的儿子
-
对 求出在二进制下的最高位
对于每次询问 的 级祖先:
- 利用倍增数组先将 跳到 的 级祖先,设剩下还有 级,显然 ,因此此时 所在的长链长度一定 。
- 由于长链长度 ,因此可以先将 跳到 所在链的顶点,若之后剩下的级数为正,则利用向上的数组求出答案,否则利用向下的数组求出答案。
(摘自https://www.luogu.com.cn/blog/xht37/solution-p5903)
HOT-Hotels
经典问题,求树上两两距离相等的三元组个数。
设 表示 的子树中到 距离为 的节点数,转移比较简单:
再设状态 表示在 的子树中选两个点,它们到其 lca 的距离为 且 lca 到 的距离为 的方案数。
转移:
这里的 表示 被 之前的儿子更新过的值。
答案的统计则要分两种情况,第一种情况,三个点都在同一个子树内,大概长这样:(图片有空再补)
对答案的贡献为
第二种情况,三个点有两个在子树内,另一个为祖先节点,对答案的贡献就是 。
至此我们就有了 的解法。
考虑长链剖分。记 的重儿子为 ,我们发现 的信息可以直接从 继承来,即 ,这个可以用指针 实现。对于 的轻儿子 ,我们在 中枚举 转移即可。这样整个过程的复杂度就是所有长链的长度和,时空复杂度均为 。
CCPC-Final 2019 G
题意
给定一棵 个节点树, 号节点有一个标记,两个人轮流移动这个标记到另一个节点,每次移动的距离必须严格比上一次对手移动的距离长。求有多少个包含 节点的联通子图,使得这个游戏后手必胜。
。
做法
博弈就是纸老虎,不难看出,其实就是求有多少个子图使得 节点是直径中点。
先设计这个恶心的 dp。
表示以 为根且最大深度为 的子图方案数。
转移:
其中 , 表示处理到当前儿子之前 的值,这是一个滚动的过程,下文的 同理。
考虑在 节点周围统计答案,再设计一个更恶心的 dp。
-
表示以 为根且最大深度小于 的子图方案;
-
表示以 为根且仅有一个儿子中的最大深度为 的子图方案;
-
表示以 为根且有两个或两个以上儿子中的最大深度为 的子图方案。
转移:
观察这个转移,我们发现所有转移都和深度有关,于是想到用长剖优化。处理当前节点 时,继承 重儿子的信息,再枚举每个轻儿子所在长链,更新 的信息。式子最后面的那个西格玛,枚举 的时候维护一个前缀和就好了。
不过还有一个问题,根据转移,处理 时所有的 都会被轻儿子更新,但我们又不能枚举到 (否则复杂度有变成 了),不过再仔细观察,我们发现当 时,后面的那个西格玛就是一个定值,这就相当于给 的 都乘上一个定值,所以打个懒标记就好了,用到的时候再下传懒标记,这样就保证了总的复杂度 。
的转移也就同理了,打懒标记即可。
luogu P1273
的树上背包
首先把所有节点按后续遍历重新标号
设 为前 个点,背包容量为 的最大价值。设 为以 为跟的子树大小。那么 就会从 和 转移过来。
loj 也有这个模板题 https://loj.ac/p/160
换根 dp
CF708C Centroids
题意
给定一颗树,你可以删去一条边,再加入一条边,保证改动后还是一棵树。
求每个点是否可以通过改动成为这颗树的重心。
要判断 是否能成为重心,我们就是要在 的大于 的子树中( 为根节点)摘下来不超过 的最大的子树,接在 上,再判断这个子树剩下部分是否不大于 。
我们需要 dp 求出一个 表示在 的子树中不超过 的最大子树。
转移大概就是
不过,具体求 的时候,我们不仅要求出最大值,还要求出一个次大值,这样当我们从 换根到 的时候,如果 的最优决策在 的子树中,我们就要用次大值来更新 。
这个同时记录最大值和次大值的方法应该是换根 dp 的一种常见思路,下面两道题都是如此。(感觉这几道换根写起来细节都挺多的)
P3647 这个题还是不错的,但可能换根做多了就比较套路了,我做这题调了一整个下午
P4292 [WC2010]重建计划
看到这个式子一定是分数规划,二分之后就是求树上包含边数在 和 之间的权值和最大的路径。
一个很简单的 dp 就是 表示以 为根子树中以 为一个端点的长度为 的路径的最大权值,然后就有
这个转移长剖就完了,不过统计答案时还有一个边数在 和 之间的限制,这是一个区间查询,所以想到用线段树维护长链。dp 过程中,把 的轻儿子信息转移到 时顺便统计经过 的路径对答案的贡献,然后这题就做完了。复杂度 。
这个题看似复杂,但每一步都非常清晰,不可能做不出来。
矩阵加速 dp
P2579 [ZJOI2005] 沼泽鳄鱼
注意到 的最小公倍数为 ,计算 个转移矩阵,乘起来之后求一个 次幂,在乘上前 个矩阵就是总的转移矩阵
P3216 [HNOI2011] 数学作业
状态转移方程:
其中 。
矩阵加速:
P2886 [USACO07NOV] Cow Relays G
广义矩阵乘法 ( 也一样)依然满足结合率可以做快速幂,本题就是矩阵加速 floyd。
P3502 [POI2010] CHO-Hamsters
一个串接到另一个串后面多出来长 的一截,看成连了一条长 的边,这个过程用到 KMP。
转化成图论模型后就与上题类似了。
P4095 [HEOI2013]Eden 的新背包问题
奇妙的 cdq 分治+多重背包
不选 物品的答案的贡献是由 和 的物品产生的,所以就可以 cdq 求两边对 的贡献,具体地,
- 先把 的物品加入背包
- 递归
- 还原(去掉 的物品)
- 把 的物品加入背包
- 递归
复杂度就是 。 是背包容量。
附:单调队列优化多重背包代码
void add(int w,int v,int s){ //加入重 w,价值为 v 的物品,数量为 s
for(int i=0;i<w;i++){
deque<pair<int,int> > q;
q.push_back(make_pair(0,f[i]));
for(int j=1;j*w+i<=m;j++){
while(!q.empty()&&q.back().val<=f[j*w+i]-j*v) q.pop_back();
q.push_back(make_pair(j,f[j*w+i]-j*v));
while(!q.empty()&&q.front().id<j-s) q.pop_front();
f[j*w+i]=max(f[j*w+i],q.front().val+j*v);
}
}
}
单调队列优化 dp & 斜率优化 dp
CF1559 D2. Mocha and Diana (Hard Version)
题意
给你两个森林,每次你可以选择两个点 在两个森林的 节点之间都加一条边,并且要保证两个森林加完边还是森林。你需要加尽量多的边并输出方案。
解题思路
官方题解不说了,也是一个挺不错的做法,用到 set 启发式合并,复杂度 。
在题解下面看到一个做法
先枚举 到 的节点,能和 连边的就都连上,这之后所有的节点都至少在一个森林里与 联通。
然后我们把所有在第一个森林里和 联通的点压入栈 ,在第二个森林里和 联通的点压入栈 。
然后不断进行一下操作:
- 如果 的栈顶在两个森林里都和 联通,把它弹出;
- 如果 的栈顶在两个森林里都和 联通,把它弹出;
- 否则,把 和 的栈顶之间连边。
总复杂度几乎是 的,只有并查集的复杂度。
按这个算法连出的边显然都是合法的,而连完之后一定有一个森林中所有点都与 连通(只有一个连通块),这样也保证了连出的边是最多的。
CF1559E. Mocha and Stars
题意
求有多少长度为 的序列 ,满足
- ;
- ;
- 。
,,。
解题思路
赛时看群里大家都说 E 很 trivial,然而我愣是一点都不会。/qd
莫反之后就是求
考虑每个 的贡献,把 都除以 。那就是,对于每个 ,求有多少个序列满足 且 。
这个直接就可以 dp 了,dp 过程用前缀和优化,单次复杂度 ,总复杂度就是 。
P4027 [NOI2007] 货币兑换
令 为第 天可以得到的最多钱数,然后列个方程解出第 天花钱买到的两种金券数 。
状态转移方程就来了:。
这个东西如果斜率优化,只能动态维护一个凸包。我记得寒假集训有一个这种斜优题,因为当时不会 cdq 也不会李超线段树,就硬写 Splay,从中午调到半夜没调出来,教练还问我为啥一道题也没补。。。此后我对 Splay 维护凸包就产生了心里阴影。
当然用李超线段树就好多了
变个形:
把 看成斜率和截距,李超线段树即可,记得离散化。
我这个代码和题解高度相似(
https://www.luogu.com.cn/record/56036585
P4655 [CEOI2017]Building Bridges
也是裸的斜优,cdq 或李超线段树都行。
luogu P4719 【模板】"动态 DP"&动态树分治
单点修改的树上最大权独立集
经典的没有上司的舞会解法,设 表示不选择节点 时,以 为根的子树的最大权独立集; 表示选择节点 时,以 为根的子树的最大权独立集。
然后
当然过不了带修的,因为每次修改一整条链上的点的 值都发生了变化,最坏是 的,然而据说过了这道题。
动态 dp
再考虑设一个 表示 的所有轻儿子中都可选可不选的最大独立集, 表示所有轻儿子都不选的最大独立集。dp 就变成了
这样以后,我们就可以把转移写成矩乘的形式了!
这里的矩阵乘法是关于 和加法运算的广义“矩阵乘法”。
考虑在重链上维护 值(线段树维护矩阵乘积即可),因为一条重链的底端一定是叶子节点,而叶子节点就储存了初始的值,我们要查询一个点的 值,只需要查询一下这个点到其所在重链底端的矩阵的乘积就可以了。
再考虑修改操作,修改一个点的权值时,该点到根的路径经过 条重链,每条重链的顶端都是其父节点的轻儿子,只有这些位置的 值会发生改变,再线段树上单点修改即可,复杂度 。
这样这道题就做完了,时间复杂度 (不过还有一个 的常数)。
P5024 [NOIP2018 提高组] 保卫王国
如果会动态 dp 的话,这题就和模板一模一样了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!