2.基础优化技巧
基础优化技巧1
三分
一般仍然是用二分+eps处理。
01分数规划
求解形如:
有
一般我们用二分答案处理,移项可得:
例题1- [HNOI2009] 最小圈
给定有向带权图,在图上找一个环,使得环上边权和除以节点个数最小,求这个最小的平均值。
。
观察到答案是比例形式,联想到
判负环注意是入队次数大于等于
写完才发现远古题的恶心之处:
- 复杂度正确的 BFS SPFA过不了,复杂度错误的 DFS SPFA能过?
- 题解所有人的算法最差复杂度达到
,这也能过? - 4202年还需要洛谷机子加强到 5s,9002年的科技就是发达,1s跑3e9。
整体二分
建议改名整体分治。注意复杂度分析。
例题2- P3527 [POI2011] MET-Meteors
有一个初始为
的序列 和 个操作,每个操作是区间 加 。你要对于每个位置 ,求出第几次操作后能使得 。
。
可以主席树二分做,但是空间被卡了。
如果只有一个国家是简单的,直接二分时间+前缀和即可。
考虑对所有国家同时进行二分, 函数 solve(l,r,<vector>v)
表示时间(操作数)的范围是
每次二分一个 mid=l+r>>1
,执行
以上是常见错误,复杂度退化成单个二分。
实际上整体二分是分治,不知道谁起的这个名字。分治过程中复杂度只能与分治区间有关。树状数组需要通过撤销清空。
复杂度
怎么这题还卡 long long
。
void solve(int l,int r,vector<int>v) {
int mid=l+r>>1;
if(l==r) {
for(int x:v) ans[x]=l;
return ;
}
vector<int>v1,v2;
for(int i=l;i<=mid;i++) {
if(op[i].l<=op[i].r)
t.update(op[i].l,op[i].r,op[i].k);
else t.update(1,op[i].r,op[i].k),t.update(op[i].l,m,op[i].k); //
}
for(int x:v) {
int tmp=Query(x);
if(tmp<lim[x]) lim[x]-=tmp,v2.push_back(x);
else v1.push_back(x);
}
for(int i=l;i<=mid;i++) {
if(op[i].l<=op[i].r)
t.update(op[i].l,op[i].r,-op[i].k);
else t.update(1,op[i].r,-op[i].k),t.update(op[i].l,m,-op[i].k); //
}
solve(l,mid,v1);solve(mid+1,r,v2);
}
补充例题 P8231 [AGM 2022 资格赛] 农场
将例题2 搬到矩形上
套用二维树状数组即可。
分治
例题3- 【模板】三维偏序
三维偏序,第一维度直接排序做。
然后分治,处理经过分治中点的区间,第二维度左右区间分别归并排序后双指针,注意这样仍满足第一维度的限制,第三维度双指针的时候用权值树状数组在左区间修改,右区间查询。
注意处理相等元素会很麻烦,所以直接去重就好了。记录一下重复元素个数。
bitset 解决 更高维度的偏序
构造01矩阵 count()
函数计算 bitset
中
构造 bitset
记录比自己小的集合即可。
时空复杂度 bitset
内存是
补充例题 :P3769 [CH弱省胡策R2] TATT
四维偏序
补充例题:P5979 [PA2014] Druzyny
例题4:分治与最短路 - P3350 [ZJOI2016] 旅行者
给你一个
的网格图,边有正边权, 次询问,从 的最短距离。 。
好题!二维题分治一维。
我们知道长成最短路的通常只能用最短路算法(网格图虽说可以
观察到
那么就可以枚举较短的那一条边作为虚拟源点了,外面再套个啥?理应想到分治,因为我们能根号对数时间内快速处理跨过一个点的询问点对。用分治进行降维。
复杂度
例题5:区间最大值分治 - CF1156E Special Segments of Permutation
给定长度为
的排列,求多少区间满足 。
不要往扫描线上想,这是经典序列分治,直接考虑中点,和前缀后缀最大值即可。比较麻烦。
还有一种不大经典的分治,考虑最大值分治,然后最大值所代表的区间是好确立的,我们 只遍历短的那一边,另一边用数据结构 数组查找。
另外可以往笛卡尔树启发式合并上想。
补充例题:CF1175F The Number of Subpermutations
求满足
各出现一次的区间 。
考虑刻画一个满足条件的区间:
- 区间最大值为
- 区间每个数都只出现了一次。
发现这样足以,前者可以区间最大值分治,后者可以用比较套路的方法:记录上一个出现的位置为
注意到区间长度实际上已经确定,因此无论左边右边都直接枚举做即可。
Wa了一次的原因是,枚举左端点,算出右端点,需要保证右端点跨过最大值。
补充例题1:P5979 [PA2014] Druzyny
倍增
常见应用:
- Floyd 判圈
- 求LCA /
级祖先 - ST 表
要求:具有结合律,不要求可减性。
例如:
其正确性由 二进制表示的唯一存在性给出。
例题6:国旗计划
环上给定
个没有包含关系的区间 ,求必须选第 个区间的情况下,还需要选多少区间才能使得 全部被覆盖。
首先断环为链,对于本题,要么处理区间,要么处理位置。
处理区间的话,我们有显然的贪心策略:如果按右端点排序,跳到最后一个左端点小于当前区间右端点的区间。这个可以简单预处理得到。
然后需要从一个区间不断跳,直到跳到这个区间左端点右边,我们有
哈希
通过把某个对象通过对其特征的描述映射为一个值来去重/判断相等。
关键在于抽象其关键信息,以构造适当的哈希函数。
字符串哈希
把字符串看作 B (B>26) 进制数,随机选择一个大质数
自然溢出容易撞的原因是:如果哈希函数构造不当导致很多偶数乘进来,例如结果为一个
例题7 异或哈希:[ABC250E] Prefix Equality
给定两个序列
,多次询问,每次给出 ,判断 的前 项和 的前 项构成的集合是否相同。(集合不可重)
显然是和顺序无关的函数,而且我们只在第一次出现的地方修改哈希即可。
常见的套路就是每个值随一个大数,异或起来。
如果拓展到区间,放到主席树上就ok了。
Trie
例题8:P3065 [USACO12DEC] First! G
给定若干字符串,你可以任意规定字符间的偏序关系,求有多少字符串能够成为字典序最小的字符串。
首先只有作为前缀的字符串才有可能是答案。
建出 trie 树,一个串能成为答案,那么从根到这个节点,会形成若干偏序关系,我们需要判断这个偏序关系是否合法。
后者是经典图论模型,偏序关系连边后判断是否成环即可。可以用拓扑排序。
例题9:P4551 最长异或路径
给定一棵树,边有边权,求两点间路径异或和最大是多少。
树上路径异或直接前缀和,甚至不需要求 lca。
问题转换为
注意全局加一是从小往大插,其它都是从大往小插。
例题10:CF888G Xor-MST
有一张
个点的完全图,边权为 ,求MST。
妙妙题。
实际上是 B开头的 MST 算法,考虑初始有
每次连通块个数至少减半,所以复杂度是
对于这个题,我们建出 01trie 后,如何找最小边?这两个点的 lca 一定尽可能深,所以 dfs 一遍tire,从下往上,找到一条最小的边,合并每个点的左右子树。根据 B-算法,这样做是正确的。不需要 dsu 等其他东西。
如何找到最小的边?可以遍历较小的一棵子树,在另一棵子树查询,复杂度类似启发式合并。
也可将原数组排序后再建树,这样树上每个点都对应原数组一段区间。直接遍历即可,每个点只会被其祖先访问,01trie深度是对数级别的。总复杂度
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现