2024 暑假训练记录
2024 暑假集训记录
Day 1 - 7.7
教练发了 2015 BJ JL HN 省队集训,大概把 BJ 的题顺了一遍,感受是十年前的题目都好板啊...
ppt 还没来得及看,只简单看了几个
Day 2 - 7.8
继续看 BJ 省队集训题,写题解。发现即使很板,但是数据范围好鬼啊...
我是不是该写几个板子复健了(?
Day 3 - 7.9
上午学习 Splay,下午写树套树,已经没有码力了/kk
经过一下午的努力我的树套树从 70 变成了 80(笑
Day 4 - 7.10
题目质量的确不高,不过 std 写的不错。
queue
求长为 n,不包含 111
和 101
的 01
串数量,答案对 10007 取模。
数据范围
key: 矩阵优化 dp
记录后两位的值,写出转移矩阵,直接矩阵快速幂就做完了。
神奇的 std:
paths
n 个点的树,树上有 m 条路径,问最多能选几条不相交路径。
数据范围:
赛场做法:树剖 + 树上 dp
设
枚举以 u 为 lca 的路径 (a,b),记路径上的点为
把求和号拆开,得到两个和都可以用树剖维护。
注意后面那个不包括 u。
std:贪心
若路径相交,选 lca 深度较大的一定不劣。
感性理解,正确性大概比较显然。
palacinke
n 点 m 边的有向图,每条边上有四种货物中的某几种。经过一条边需要 1 分钟,买货物需要 1 分钟(不管买了多少)。从点 1 出发,最终回到点 1,四种货物必须都买。求在 T 分钟内,有多少种方案。
数据范围
sub1&2 :暴力状压 dp
复杂度
sub3 :矩阵优化 dp
压成一个
sub4 所有边都是四种都有:随便走 - 完全不买
key:拆点,把“买东西”拆出一条边
只需一个
正解:容斥
随便走
枚举 16 种情况,建邻接矩阵时有些买东西的边不连就行。
key:建立汇点,强制最后一次回到 1 是回到汇点
在汇点建立自环,让“不超过T”都变成 T,最后答案就是汇点的矩阵值。
ogledala
有 m 个石子,初始已经挖掉 n 个
重复下列操作:
- 找到当前最长的一段连续的石子,若有多段,取最左边的
- 挖掉最中间的石子,若段长为偶数,则挖掉靠左那个
Q 次询问第
数据范围:
sub1&4 :堆模拟
正解:二分
初始连续段最多只能分出
种不同长度的段
由于任何时刻,一定是从长度最长的段开始分,于是我们可以顺序计算出每种长度的段都有多少个。
排序询问,利用双指针思想,定位分的是长度为 len 的段,并知道是初始第 id 段里的第 num 个 len。
知道在哪一段后,就可以二分出位置。因为可以计算出任意长度的连续段分出的长度为 len 的段的数量。
code:
LL f(LL nw,LL want){// 原长为 nw,能分出长为 want 的段的数量 if(nw<want) return 0; if(nw==want) return 1; if(dp.find(nw)!=dp.end()) return dp[nw]; return dp[nw]=f(nw>>1,want)+f((nw-1)>>1,want); } LL query(LL l,LL r,LL len,LL num){ LL mid=(l+r)>>1; if(r-l+1==len) return mid; if(f(mid-l,len)>=num) return query(l,mid-1,len,num); return query(mid+1,r,len,num-f(mid-l,len)); }
Day 5 -7.11
interview
给定序列
数据范围:
key:转化为值域上问题
- 二分 + 值域上滑动窗口
- 记录
表示值域 有多少个数,线段树维护
task
两台机器 A B,有
数据范围:
sub 1&2 : dp
转移平凡,注意第 i 个任务要等第 i-1 个任务开始才能开始。
正解: dp
key:最优解时间差不超过
转移同样平凡,注意数组别开小了。
diamond
有一个序列
- 若
,选择 ,结束操作 - 否则,
,且之后不能再选择
对于每个
数据范围
key:期望 dp + 分段矩阵乘
记
若干结论:
一定是全集 一定选一段后缀 单调不升- key:
(不选还有 ,更小的干脆不选)
显然可以线性递推
发现
二分出每段的右端点,check 方法就是直接判断
复杂度
control
对于
数据范围
key:转化为 使
贪心地从深度最深的不合法点开始,选择它的
神奇的事情在于,对于某个 x,最多只可能做
写法上,可以使用标记永久化的思想(类似李超线段树),但需注意 pushup 时要判好左右儿子分别被标记的情况。
Day 6 - 7.12
构造之神 —— devans
记录常见的错误,调代码的时候可以对照着排查——zzj
中午拉着全机房的人测 mbti 捏(
呈现了 mbti 多样化的局面...
CF1983B Corner Twist
唯一自己会做的题/kk
012
矩阵,每次操作:
- 选择一个至少
的矩形 - 对其中两个对角格子
,另外两个
问可否通过若干次操作,把矩阵
key1:必要条件
- 整个矩阵的和
意义下不变(每次操作 +6) - 每行 & 每列的和
意义下不变(每次操作 +3)
下证为什么是充要条件。
key2:简化操作
显然,每次操作都可以拆成若干次选
只考虑满足左上角,从左到右,从上到下依次操作。由于行列和不变,所以满足前
CF1730B Meeting on the Line
数轴上有
这题有弱智的
key1:弱化问题
如果
key2:转化问题
拆点成
CF1391E Pairs of Pairs
给定
- 找到一条点数
的路径 - 找到一个大小
的集合,对所有点两两配对,满足任意两对点的导出子图有 条边。
key:dfs 生成树没有横叉边
若存在深度
否则树的层数较小,直接每层的点两两配对,一定满足要求。
- 没有横叉边,所以导出子图最多有两条返祖边
- 每层最多扔掉一个点,而一共只有
层
三道类似的例题:
CF1508C Complete the MST
数据范围
key1:一条边赋异或和,其他全 0
- 若有边不选进最小生成树,那么直接把这条边赋成异或和
- 否则怎么赋值都一样,这样做肯定不劣
key2:考虑给定边的稠密程度
即 ,枚举哪条边赋值成异或和,直接跑最小生成树,复杂度 即给定边稀疏,一定有未赋值的边不选进最小生成树,直接当所有边权为 0,缩连通分量,跑给定边的最小生成树即可
如何求完全图删去
子问题 CF920E Connected Components?
key3:最大值
删去
从度数最大的点开始考虑,不与它联通的点很少,最多只有
CF1508D Swap Pass
平面上
- 选择两个没有连边的点
,连接 - 交换
要求线段不能有交(除端点外),给出操作方案使
保证不存在三点共线,
key1:局部问题
置换环:对于排列
,连接 ,一定得到若干个环。
只有一个置换环的排列可以用菊花图完成
操作就是不断交换根
key2:合并问题
置换环内部交换
拆解置换环
置换环之间交换合并置换环
考虑能否把所有点合并到一个置换环中。
首先连出一个菊花,显然的可以连接最外面的所有边,这样就全部合成一个置换环,且不会相交。
具体的,选择最左边的点为根,斜率排序,顺时针/逆时针合并置换环(并查集维护),若在一个置换环里就跳过,否则连边。
操作顺序是先合并置换环,再做菊花图。
CF1523D Love-Hate
数据范围
key:答案被超过
随机到某个人 x,认为答案是被他喜欢的,于是只有
扫一遍
每种态度只对它的子集有贡献,可以证明枚举子集的子集复杂度
再扫一遍
单次随机复杂度
证明枚举子集的复杂度
(二项式定理)
还有一种高维前缀和的做法是的
CF1804F Approximate Diameter
key1:
钦定一个点为起点,dfs 出最远点,这个距离一定在
key2:允许误差
正确答案 & 较优答案 单调不增
记正确答案为
重复下述过程:
- 当前有答案
,二分可行区间右端点 check(p)
:判断
为什么?
显然
总复杂度
Day 7 - 7.13
继续写昨天邹邹讲的题,还有 4 道没写/kk
Day 8 - 7.14
ak
n 堆石子
数据范围
key: 只有
每次一定拿走一个完系,且剩下的余数不会改变。
所以一共有
loser
给定
- 反转 (x,y) 路径上所有括号
- 查询
的有向路径组成的括号序列中,最长的合法子序列的长度
数据范围
key:合法括号序列的数值表示
众所周知,令左括号为
(前缀和)
已知括号序列的权值和
若
此时新的
线段树维护
注意需要维护前缀 & 后缀 的 最大值 & 最小值,细节较多。
king
- 对于每个一类物品,花费 1 单位代价收益
- 对于每个二类物品,花费 1 单位代价收益
,或花费 2 单位代价收益
每种物品不能重复买,求花费不多于
数据范围
首先 v 的数据范围是诈骗,实际最多只可能花费
sub1 :暴力 dp
sub2 :贪心
正解 :反悔贪心
key1:按照单价贪心
一类物品没啥好说的,二类物品有两种情况:
:拆成两个代价为 的物品, 和 :显然不可能只选 ,直接捆成单价 的物品
显然如果出现选不了的情况,只可能是最后剩下 1 单位代价,而下一个该选的是代价为 2 的物品。
此时有两种选择:
- 再选一个代价为 1 的物品
- 去掉一个代价为 1 的物品,选现在这个物品
注意,对于还没被选的二类物品,即使
key2:直接反悔贪心
代价每增加 1,有 4 种情况:
- 选择一个代价为 1 的物品
- 升级一个代价为 1 的物品
- 失去一个代价为 1 的物品,选择一个代价为 2 的物品
- 降级一个代价为 2 的物品,选择一个代价为 2 的物品
这种设计满足最优子结构,直接堆维护反悔贪心即可。
Day 9 - 7.15
还没上班就想下班的 devout 小姐姐可爱捏
maxmex
给定序列
数据范围
看懂本题的 std 太费心力了/kk
记 maxmex 为
从小到大枚举
在平面直角坐标系上表示一个区间
我们希望知道
我们要求对于
如图所示,所有矩形都“挂在”
当我们加入一个新的区间,会有一些旧的绿色线段被覆盖,产生新的绿色线段(黑色的线段被覆盖,产生新的两个绿色线段)
具体维护过程看代码注释/kk
map<int, int> mp; priority_queue<int, vector<int>, greater<int>> Q, DQ; // mp 维护阶梯的所有顶点 (l,r) // 可能成为答案的一定是阶梯的拐点 // Q 维护 r-l 最大值的最小值,DQ 维护应被删掉的 r-l 较小值 void ins(int l, int r) { //it 代表存横坐标 l 的顶点的指针 auto it = mp.lower_bound(l);//找到横坐标 >=l 的第一个顶点 if (it == mp.end() || it->first != l) {//如果 l 没有顶点 if ((--it)->second >= r) return;//如果前面的顶点比 r 高,那么 (l,r) 一定在阶梯内 Q.push(it->second - l);//l-1 成为新的拐点,塞进答案堆里 it = mp.insert(make_pair(l, it->second)).first;//新建一个顶点,不赋值成 r 只是为了方便 } if (r <= it->second) return; auto jt = next(it);//把 l 右边的拐点,被 [l,r] 覆盖的删掉 if (jt == mp.end()) {it->second = r; return;} DQ.push(it->second - jt->first); while (jt != mp.end() && jt->second <= r) {// 所有高度低于 r 的拐点都会被删掉 auto kt = next(jt); if (kt != mp.end()) DQ.push(jt->second - kt->first); mp.erase(jt); jt = kt; } it->second = r;//修改一下纵坐标 if (jt != mp.end()) Q.push(it->second - jt->first);//l 右边的新的拐点 } int main() { cin >> N; for (int i = 1; i <= N; ++i) { int a; cin >> a; pos[a].push_back(i); } for (int i = 1; i <= N + 1; ++i) mp.insert(make_pair(i, i - 1)); for (int rep = 1; rep <= N; ++rep) Q.push(-2); for (int i = 0; i <= N; ++i) { if (pos[i].empty()) ins(1, N); else for (int j = 0; j != pos[i].size(); ++j) { if (j == 0) { if (pos[i][j] > 1) ins(1, pos[i][j] - 1); } else { if (pos[i][j] - pos[i][j - 1] >= 2) ins(pos[i][j - 1] + 1, pos[i][j] - 1); } if (j == pos[i].size() - 1 && pos[i][j] < N) ins(pos[i][j] + 1, N); } while (!DQ.empty() && DQ.top() == Q.top()) {Q.pop(); DQ.pop();} if (Q.empty()) break; ans[Q.top() + 3] = i + 1;// Q 维护的是 r-l,实际拐点是 l-1,于是实际f(k)=i 区间右端点是 (r-l)+2 } for (int i = 1; i <= N; ++i) ans[i] = max(ans[i], ans[i - 1]); int Q; cin >> Q; while (Q--) {int k; cin >> k; cout << ans[k] << '\n';} return 0; }
klein
每个方向的翻转最多只会发生一次,于是可能路径是
Day 10 - 7.16
CF1983D Swap Dilemma
key: 无重复序列交换两个数,逆序对数量奇偶性改变。
结论是 A B 可重集相同且逆序对数量奇偶性相同。
证明:
- 考虑只交换
,显然与原问题等价 - 可以用若干次操作把 A 换成单增序列,此时 B 的逆序对数量是偶数
- 令
,对 B 做冒泡排序,因为逆序对是偶数,所以会交换偶数次,相当于 A 没有交换。
习题 CF1591D Yet Another Sorting Problem
CF1628C Grid Xor
构造题,key 是用两个相邻点的结构覆盖矩形,手玩找规律。
大概形如这个样子:
x x x o o x x x
答案长得就是四边往里堆金字塔,需要分讨 mod 4 的余数。
CF1656E Equal Tree Sums
树上构造题主要从几个方面考虑:
- 叶子归纳
- 直径
- 二分图
- 度数
- 重心
子树平衡让人想到根据度数来赋值。
构造:二分图染色后,白点赋
证明:
- 每条边两端一白一黑,总贡献 0
- 删去一个点只会给每个连通块加上
的贡献,仍然相等。
CF1943C Tree Compass
有关树上距离,可以想到直径。
构造:取直径中点,对称染黑(偶数时分讨 mod 4)
证明:直径上点挂的其他链不长于直径上的链,于是一定可以被覆盖到
习题:
CF1736D Equal Binary Subsequences
必要条件:01 个数均为偶数。
连续 01 段长度为偶数的序列是好分的。
把原序列两位一断,00
11
不管,01
和 10
共有偶数个,按 0 1 0 1 ...
地选,循环移位后就消没了。
CF1665D GCD Guess
询问 30 次自然的想到按位确定,构造平凡
还有神奇的 CRT(中国剩余定理)做法,找一组互质的数,枚举余数找到同余关系,CRT 解方程即可。
CF1553F Pairwise Modulo
对于每个
key:调和级数
CF1548C The Three Little Pigs
q 次询问,给定 x,求
key:考虑
记
线性 dp 即可。
CF1553G Common Divisor Graph
key1:答案
key2:质因子个数
每个质数建新点,每个
答案
枚举
对询问的两个点所在并查集枚举质因子对,check 是否连通。
复杂度
CF1689E ANDfinity
先 merge 一遍,得到若干不重复的集合。
发现 lowbit 最大的数 -1 就可以 merge 完所有数。
但是如果出现 1 4 4 4
这种情况,需要再把一个数 +1,变成 101 100 011
就可以了。
CF1617E Christmas Chocolates
发现每次操作相当于保留 lowbit,翻转前面所有。
于是总操作次数上界就是
对每个 i,k,把
答案等价于求树的直径,可以容易的求出某个数到 0 的距离(暴力跳父亲),然后再求一遍最远点到所有点的距离就行了。
复杂度
CF1526B I Hate 1111
发现只有 77
777
是有用的,其他都可以用它们表示出来。
由 NOIp 2017 小凯的疑惑 可知
key:
先干掉一个 7,只用 11
111
表示,前面暴力递推,后面直接算等差数列求和即可。
AT_abc232_e [ABC232E] Rook Path
相对于
记
AT_arc143_e [ARC143E] Reversi
考虑菊花图,一定是先把所有正面朝上的叶子移除,此时若根是正面朝上,移除根,再移除剩下的叶子;否则无解。
启发得到结论:对于一个叶子,若正面朝上,则一定比父亲先被移除,反之亦然。
建边跑拓扑排序即可。
无解情况当且仅当整棵树有偶数个正面朝上的节点。
证明(数归反证):若有偶数个正面朝上的节点,找到其中一个,它一定有至少一棵子树有奇数个正面朝上的节点,删掉这个点后有偶数个正面朝上的节点。递归下去,最后只剩一个节点,但要有偶数个正面朝上,故这个独点一定反面朝上,无解。
AT_wtf19_d Distinct Boxes
step1:转化条件
step2:希望
令
step3:二分
step4:二分
step5:二分
复杂度
Day 11 - 7.17
难度排序
/kk
what
- 加入一个权值为
的点 - 全局加
- 查询权值
的人的数量。
数据范围
key:根号分治
维护所有答案 枚举所有 的数
全局加可以打标记,加入新点减去标记,一定在
复杂度
HNOI2003 年份
给定数字串
- 数值严格递增
- 最后一个数尽量小的前提下,字典序尽量大
数据范围
错误想法:二分 + 贪心
有时我们会“牺牲”一些后面的数,让前面的数更大,从而字典序更大
字典序大和单增是矛盾的,需要找到平衡,而 dp 就是干这个的。
key:双向 dp + hash 子串比较 + 线段树优化决策
设
这样我们正着推一遍
子串比较可以用 hash 二分求 lcp 优化到
另外,这两个式子满足决策单调性,即值单调递增。
考虑
由于是单点查询,标记可以永久化,不需要上下传,减少常数。
注意多测清空不可以用 memset
au
定义广义菊花图:
- 只有一个重心
- 以重心为根,所有点的深度不超过 3
- 深度为 2 的点至少
个 - 所有深度为 2 的点,子树大小不超过
问
数据范围
简单计数题,分层考虑。
- 根节点只有 1 个,n 种情况
- 设第二层有
个节点, 种情况 - 第三层考虑容斥
- 若没有限制,有
种情况(每个点随便选父亲) - 若不合法,有且仅有一棵子树有
,有 种情况(先选不合法子树,其他叶子随便选父亲)。
- 若没有限制,有
预处理组合数和幂次,复杂度
WC2024 镜像
给定
- 存在一个正实数
,使得存在一个单增序列 ,满足
求“好的”二元组个数。
f1:线段树分治 + 可撤销并查集
枚举 T,找最大合法
考虑如果知道
认为我们是在两行点上做,于是我们希望知道这个图的连通性。
注意到某条边是否联通(某个转移能否实现)只取决于
另外注意到对于某个
我们可以从小到大枚举
某条边的存在条件对应
可撤销并查集:不进行路径压缩,通过启发式合并保证树高
级。因为需要维护子树大小,只能按照加入顺序从后到前删边 线段树分治:把修改、询问挂在时间轴上,从根节点往下遍历,把经过的标记都修改掉,从儿子回到父亲的时候撤销修改
线段树分治 + 可撤销并查集是非常经典的用法,因为线段树分治保证了撤销是从后到前的
f2:矛盾情况 合法区间
枚举
,考虑是否存在合法
令
因为
且 且 , 且 且 , 且
考虑后两种情况相当于如果存在
那么合法当且仅当这些区间的交不是空集,因为若区间交非空,那么
求区间交相当于求
显然当
预处理
Day 12 - 7.18
name
给定
数据范围
key: hash 预处理 + 状压 dp
状压 dp 是显然的,关键在预处理包含和前后重叠。
hash 是判断字符串相等的重要工具,尤其是有很多子串的时候/kk(要记住啊/kk
可以二分 + hash 或者直接暴力,复杂度是对的。
注意有很多相同字符串的时候要保留一个,不能都删掉。
chk
- 每条边最多出现在一棵生成树中
- 对于任意一组
,在任意两棵生成树上, 的路径除 外没有相同节点。
给出
数据范围
key:
因为如果跳到相同的就寄了,如果跳不到就最多只会跳 n 次,于是枚举
tree
定义和 t2 相同,要求构造
- 满足第一个要求,且不满足第二个要求
- 同时满足两个要求
找到一棵合法的,然后转一圈。
第 1 种好构造,就是在圆上左右跳,一定没有重边
注意到
菊花图是不行的,因为边不能重复,这样会让一个点变成独点。
但是把两个菊花图拼起来就可以了。
我们让
grid
支持加减边,在线维护 2 行
数据范围
key:线段树维护区间四个端点的连通性
合并平凡,查询注意
Day 13 - 7.19
CF1404E Bricks
给定
数据范围
key:边点转化 + 二分图最大独立集
认为我们用大于
把能擦除的网格边建点,不能同时被擦掉的点建边,求二分图最大独立集。
大概就长成这个样子:
CF1482H Exam
给定若干字符串
是 的子串- 不存在
使得 是 的子串,且 是 的子串。
数据范围
key:AC 自动机 fail 树
固定
考虑没有第 3 个限制。
是 的子串当且仅当 fail 树上 的 end 是 某个节点的祖先。 显然子串等价于“某个前缀的后缀”
于是建出
实现可以 倍增 + 树上差分 / 树剖,合并
. . . . . . . . . . . . . . . . . . . . . . . .
考虑增加第 3 个限制,相当于只有 fail 树上最近的 end 可能产生贡献,先把这些答案串拿出来。
对一个答案串,我们希望知道它是不是某个答案串的子串,也就是希望知道它的子树内是否有某个答案串的前缀。
从后到前扫描
P3236 [HNOI2014] 画框
给定一个完全二分图,每条边有权值
数据范围
类似 devans 讲的 AT_wtf19_d Distinct Boxes 这道题,我们希望
考虑把二维决策扔到平面上,不妨横坐标
分治求凸包上的点,考虑找距离直线 AB 最远的点 C,保证分治 AC BC 后凸包上的点在 AC BC 下方。
记直线 AB 为
可以证明值域
P7880 [Ynoi2006] rldcot
给定
数据范围
考虑一个点会在什么时候产生贡献,显然是左右儿子都有
把子树内节点按编号排成一列,形如:o o # # o # o # #
,发现只有相邻的才有用,因为如果不相邻的都在区间内,那么相邻的肯定也在区间内。
相邻节点可以贡献答案的区间要求形如
找相邻点对的过程使用启发式合并,复杂度正确。
Day 14 - 7.20
network
毒瘤题。
给定
当且仅当 存在边
数据范围
walk
傻子题,与 palacinke 几乎完全相同,甚至弱于这道题
key:容斥 + 矩阵乘
data
给定
定义
最大化
数据范围
key: 枚举
我们希望知道
const int N=1e5+5; vector<int> e[N]; #define pb push_back const LL INF=1e18; int n; LL f[N][2];int rp[N][2]; struct node{ int id,p;LL val;int op; node(int id=0,int p=0,LL val=0,int op=0): id(id),p(p),val(val),op(op){} bool operator<(const node &b) const{ return p<b.p; } }; vector<node> vec; bool in[N][2]; int siz,tot;LL ans; inline void add(node x){ in[x.id][x.op]=1; if(!in[x.id][x.op^1]) ans+=x.val,tot++; else if(f[x.id][x.op]>f[x.id][x.op^1]) ans+=f[x.id][x.op]-f[x.id][x.op^1]; } inline void del(node x){ in[x.id][x.op]=0; if(!in[x.id][x.op^1])ans-=x.val,tot--; else if(f[x.id][x.op]>f[x.id][x.op^1]) ans-=f[x.id][x.op]-f[x.id][x.op^1]; } inline LL ff(int xi,int u){return (LL)(xi+999)/1000*666*u;} inline void calc(int u,int op,int xi){ tot=0;ans=0;int r=-1; rep(l,0,siz-1){ while(r+1<siz && vec[r+1].p-vec[l].p<=xi) add(vec[r+1]),r++; if(tot==(siz+1)/2) f[u][op]=max(f[u][op],ans-ff(xi,u)); del(vec[l]); } } inline void solve(int u,int op){ vec.clear(); vec.pb(node(u,rp[u][op],rp[u][op],op)); for(auto v:e[u]){ vec.pb(node(v,rp[v][0],f[v][0],0)); vec.pb(node(v,rp[v][1],f[v][1],1)); } sort(vec.begin(),vec.end()); siz=vec.size(); int mx=vec.back().p-vec.front().p; for(int xi=0;xi<=mx+1000;xi+=1000) calc(u,op,xi); } inline void dfs(int u){ if(e[u].empty()){ f[u][0]=rp[u][0],f[u][1]=rp[u][1]; return ; } for(auto v:e[u]) dfs(v); solve(u,0);solve(u,1); }
Day 16 - 7.22
CF1634E Fair Share
平均分成两部分,这个限制非常像二分图。
序列内相邻位置连边,每个值出现顺序相邻连边,显然两种边分别内部不会相连,于是不存在奇环。
CF1638D Big Brush
key:最后一次操作一定留下一个
倒着从后往前 bfs 即可。
CF1630D Flipping Range
key1:所有
辗转相减,最后剩下的就是
key2:考虑
显然我们可以同时翻转相隔
于是按
但是会存在若干反例,如 -9 -9 -9 1 1 1
,此时翻转
CF1712E2 LCM Sum (hard version)
考虑去掉
显然正整数解只有 3 种:
后两种直接枚举
关键在维护
key:离线 + 倒着枚举倍数
考虑所有数不相同,枚举倍数是
从大到小枚举倍数,维护组合数,在左端点处理询问,直接区间求和即可。
CF1798F Gifts from Grandfather Ahmed
key:EGZ 定理
任意
个数,都能选出 个数,满足它们的和是 的倍数
按
CF1442C Graph Transpositions
key:分层图 + 按答案大小分治
显然,在图上单纯走边最多走
如果翻转次数
于是对前 20 次翻转建分层图,在图上跑最短路。
对翻转次数多的,直接建一个正图一个反图,层间连边权 1e9,这样就会尽量少翻转,最后最短路除 1e9 就知道翻转了多少次。
两种答案取最小值即可。
CF1830D Mex Tree
key:树上 dp + 答案下界优化状态
首先 mex 只有
考虑
发现有一种显然的较优构造是 01 染色,答案大概是
这样就能把背包的第三维优化到
CF1794E Labeling the Tree with Distances
key:hash判可重集相同 + 换根
条件等价于以 x 为根,所有点深度的可重集与给定的权值集合相同,但允许有一个自选。看上去很奇怪,但是深度
判断可重集相同最常用的就是哈希,常见的好维护的哈希方式是
用换根 dp 维护子树内和子树外的哈希值,转移平凡。
CF1733E Conveyor
看上去是找规律但它其实是 dp /kk
发现经过某个格子的 slime 一定是 向右/向下 交替,即若有
于是我们就可以从左上角开始递推出到过每个格子的 slime 数量。
求时间
求前
为什么是
CF1768E Partial Sorting
key1:
前
:已经排好序了,共 种 :前 个/后 个已经排好,共 种 : 在前 个里/ 在后 个里 :全部, 种
关键在算
key2:枚举中间
设
写成式子就是
于是
Day 17 - 7.23
太不好了,这么多签到题(敲打
好像有点不敢想了呢/kk
不应该这样的,正解是要努力想努力写的/kk
dream
数据范围
key:树上启发式合并
重链剖分 + 线段树
线段树维护
理论复杂度是与
实测需要 3.5 - 4.5 s,也就是说比正解多了一个 2 左右的常数,差不多就是
不过我的线段树实现不算很快,实现好一点的话可能卡进 2s。
长链剖分 + ST 表
发现前面这种做法的瓶颈在遍历轻子树,而我们这道题其实不太关注 size,反而与深度息息相关。
使用长链剖分,st 表维护距离点 x 在
st 表使单次插入和修改均摊复杂度为
travel
一棵无限大的完美二叉树(
数据范围
显然是按位跳,复杂度
令一个平面对应它正上方的点。
发现一定是往 lca 的下面/左边/右边跳,分别计算三种的答案。
每次穿过一条树边一定是往相邻的左/右平面跳,可以计算这个平面对应的点。从而可以计算从一个点向上跳到某个平面,穿过的边的个数。(注意,在同一棵子树内,显然是往编号更小的平面跳更优)
LL LCA(LL a,LL b){ if(a<b) swap(a,b); while(floor(log2(a))>floor(log2(b))) a>>=1; per(i,63,0) if((a>>i)!=(b>>i)) return a>>(i+1); return a; } LL left(LL a){ return a>>__builtin_ffs(a); } LL right(LL a){ if(__builtin_popcount(a+1)==1) return -1; return left(a+1); } bool in(LL a,LL b){ if(a==0 && b==-1) return 0; if(a==-1 && b==0) return 0; if(b==-1 || b==0) return 1; rep(i,0,63) if((a>>i)==b) return 1; return 0; } int jump(LL a,LL b){ if(a==b) return 0; int res=-1;//first step no cross while(a!=b){ LL L=left(a),R=right(a),nxt=LONG_LONG_MAX; if(in(L,b)) nxt=min(nxt,L); if(in(R,b)) nxt=min(nxt,R); a=nxt;res++; } return res; } void work(){ LL c=LCA(a,b); int u1=jump(a,c),u2=jump(a,left(c)),u3=jump(a,right(c)); int v1=jump(b,c),v2=jump(b,left(c)),v3=jump(b,right(c)); int ans=min({u1+v1,u1+v2+1,u1+v3+1,u2+v2,u2+v1+1,u2+v3+1,u3+v3,u3+v1+1,u3+v2+1}); printf("%d\n",ans); }
runplus
数据范围
显然需要与
key:虚树上统计答案
虚树上的点可以直接跑多源 bfs 求最短路
实树上的点分为两类:
- 虚树节点的实子树
- 虚树边上点的实子树
实树上的点一定是先到虚树上,再到最近的打卡点。
虚树节点的贡献容易计算,边上参考链需要二分出向上/向下跑的分界点,预处理一大堆东西统计答案即可。
fight
给定多项式
数据范围
全场最大诈骗题!
本质就是求
Day 18 - 7.24
merge
给定序列
- 合并
和 为
求至少多少次操作,能把序列变成回文的。
key:双向贪心
mahjong
题面太长,不想写了。
key:dp预处理
只关心一共有几种关键牌,有没有一对,还剩多少张牌。直接 dp 转移即可。
build
给定序列
- 选择区间
和 正整数 ,对
求最少需要几次操作,使得
数据范围:
转化成从
key1:区间加减想差分
在
key2:能找到一种最佳方案,每次操作让一个
对于每次操作,连边
记
count
给定一棵
数据范围
key:树上启发式合并 + 换根
转化为求对每个
结合换根,需要求一个子树内的答案和一个子树外的答案,推推式子即可。
Day 20 - 7.26
P4494 [HAOI2018] 反色游戏
一张
问有多少种选择方案使得最后点都是白色的。对每个点删除这个点以及与其相连的边之后询问。
数据范围
有解的充要条件:每个连通块异或和均为 0
视黑色为
随便找一棵生成树,从叶子开始依次确定,最后由于异或和为 0,根一定满足要求。
那么生成树外的边就可以随便操作,方案数是
求点双维护连通块数量即可。
P7099 [yLOI2020] 灼
桃之夭夭,灼灼其华
一个数轴,有
问期望经过的轮次数
数据范围:
显然
可以写出式子
变形成递推式
矩阵快速幂优化是会 T 的,需要预处理
另解:变形成
P3679 [CERC2016] 二分毯 Bipartite Blanket
给定一个二分图,两边分别有
数据范围
key1:考虑只有一边——hall定理
点集 S 可以被一个匹配覆盖,当且仅当:
,与 内点有边相连的点集不小于
key2:两边分别可以被覆盖,则整体可以被覆盖
证明:
将左部点和右部点对应匹配取并,每个点度数不超过 2,即只存在环或链。
环和长度为奇数的链是容易的,直接隔一个取一个。
长度为偶数的链一定有一个端点不在点集里,否则左部和右部点数量不相同,去掉这个端点就是长度为奇数的链了。
左右分别枚举点集判断能否被覆盖,最后排序双指针统计答案。
P5287 [HNOI2019] JOJO
一个初始为空的字符串,每次可以在后面添加一些相同的字符,或者回退到某个先前状态,求每次得到的字符串的kmp的next数组每位之和。
数据范围:询问
第一想法是直接跳 next 找与新加入字符相同的段,但这样复杂度是字符集均摊的,会被卡掉。
考虑优化跳 next 的过程,发现如果每次跳的很短那么一定是有循环节,而直接跳过所有循环节是不会有问题的。如此,每次跳 next 长度至少减半,复杂度就是严格
离线询问建出类似 trie 树的东西,直接递推求即可。
P6729 [WC2020] 有根树
给定一棵
对于一个点
询问每次修改完所有可能连通块权值最小值。
数据范围
首先
key:每次操作答案最多
记
加入一个点影响是路径 +1,可能导致
删除同理,最终答案变化不会超过 1
一个简单的想法就是用树剖 + 线段树维护 size,复杂度
. . . . . . . . . . . .
考虑对于每一条重链,最多只会有1个位置,两侧的点一个在连通块内一个在连通块外。
记录所有重链上这两个点,若都在
用链表对每一种
UOJ 513
给定
- 对一个简单环
- 将点集分成两份并对介于两者之间的边
询问能否在 次操作内将边权清 0
数据范围
key:看到简单环想生成树
任意环操作一定可以转换成若干非树边对应环的操作。
每次操作不改变节点度数奇偶性,于是若所有点度数都是偶数,则可以用不超过
第二种操作可以转化成单点操作,一共
要求解异或方程组,使用线性基即可。
UOJ 574
对于一个时刻,如果恰好有
求节省电量期望
数据范围
key1:第
具体我也不会算,反正是对的
key2:容斥 + FFT
对于
说是可以拆开组合数然后卷积/kk
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】