2.基础优化技巧

基础优化技巧1

三分

一般仍然是用二分+eps处理。

01分数规划

求解形如:

n 个物品,每个物品有两个权值 a,b,选出一些物品,使得两个权值和的比值最大或最小。也就是另 wi=0/1,最大化:

wi×aiwi×bi

一般我们用二分答案处理,移项可得:

wi(ait×bi)0

例题1- [HNOI2009] 最小圈

给定有向带权图,在图上找一个环,使得环上边权和除以节点个数最小,求这个最小的平均值。

n3000,m1000,wi107

观察到答案是比例形式,联想到 0/1 分数规划。考虑把边看作 a,点看作 b,二分一个答案后,边权 v 重新赋值为 vt,使用 SPFA 找负环即可。找到负环说明 mid 仍能再小。

判负环注意是入队次数大于等于 n

写完才发现远古题的恶心之处:

  • 复杂度正确的 BFS SPFA过不了,复杂度错误的 DFS SPFA能过?
  • 题解所有人的算法最差复杂度达到 O(m2nlog),这也能过?
  • 4202年还需要洛谷机子加强到 5s,9002年的科技就是发达,1s跑3e9。

整体二分

建议改名整体分治。注意复杂度分析。

例题2- P3527 [POI2011] MET-Meteors

有一个初始为 0 的序列 am 个操作,每个操作是区间 [lj,rj]kj。你要对于每个位置 i,求出第几次操作后能使得 ai>xi

n3×105

可以主席树二分做,但是空间被卡了。

如果只有一个国家是简单的,直接二分时间+前缀和即可。

考虑对所有国家同时进行二分, 函数 solve(l,r,<vector>v) 表示时间(操作数)的范围是 [l,r],序列是 v

每次二分一个 mid=l+r>>1,执行 [1,mid] 时间内的操作,看每个点是否超过限制 xi。超过则说明需要小一点,放在 vl 里,否则放在 vr 里。注意树状数组及时清空即可。

以上是常见错误,复杂度退化成单个二分。

实际上整体二分是分治,不知道谁起的这个名字。分治过程中复杂度只能与分治区间有关。树状数组需要通过撤销清空。

复杂度 O(nlog2n)

怎么这题还卡 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矩阵 Sp 表示第 p 维度的偏序关系,不难发现 S1(i,j)\andS2(i,j)... 即为 i,j 在所有偏序上的关系。只需用 count() 函数计算 bitset1 的个数即可。

构造 S 的过程也比较简单,对于每一维先排序,再用 bitset 记录比自己小的集合即可。

时空复杂度 O(n2/ω),ω=64,时间上常数较小,空间上比较紧:**牢记NFLS bitset 惨案:1个长度为 Nbitset 内存是 N×4/ω,ω=32 **。当 N=1010 时达到了 1G+,小心MLE保龄。

补充例题 :P3769 [CH弱省胡策R2] TATT

四维偏序

补充例题:P5979 [PA2014] Druzyny

例题4:分治与最短路 - P3350 [ZJOI2016] 旅行者

给你一个 N×M 的网格图,边有正边权,Q 次询问,从 (x0,y0)(x1,y1) 的最短距离。N×M104,Q105

好题!二维题分治一维。

我们知道长成最短路的通常只能用最短路算法(网格图虽说可以 dp,但显然不好做),我们曾做过:[GXOI/GZOI2019] 旅行者,使用二进制分组的方式建虚拟源点跑 log 次最短路。那么这道题能否用类似的方式呢?

观察到 N×M104CandyBar 曾在根号重构中讲过类似的处理套路,即 min(N,M)100

那么就可以枚举较短的那一条边作为虚拟源点了,外面再套个啥?理应想到分治,因为我们能根号对数时间内快速处理跨过一个点的询问点对。用分治进行降维。

复杂度 O(nnlogn+Qn)

例题5:区间最大值分治 - CF1156E Special Segments of Permutation

给定长度为 n 的排列,求多少区间满足 pl+pr=maxi=lrpi

不要往扫描线上想,这是经典序列分治,直接考虑中点,和前缀后缀最大值即可。比较麻烦。

还有一种不大经典的分治,考虑最大值分治,然后最大值所代表的区间是好确立的,我们 只遍历短的那一边,另一边用数据结构 数组查找。

另外可以往笛卡尔树启发式合并上想。

补充例题:CF1175F The Number of Subpermutations

求满足 1...rl+1 各出现一次的区间 [l,r]

考虑刻画一个满足条件的区间:

  • 区间最大值为 rl+1
  • 区间每个数都只出现了一次。

发现这样足以,前者可以区间最大值分治,后者可以用比较套路的方法:记录上一个出现的位置为 pi,则 maxi=1rpi<l

注意到区间长度实际上已经确定,因此无论左边右边都直接枚举做即可。

Wa了一次的原因是,枚举左端点,算出右端点,需要保证右端点跨过最大值。

补充例题1:P5979 [PA2014] Druzyny

倍增

常见应用:

  • Floyd 判圈
  • 求LCA / k 级祖先
  • ST 表

要求:具有结合律,不要求可减性。

例如:O(logn) (查询)求树上路径 min,gcd

其正确性由 二进制表示的唯一存在性给出。

例题6:国旗计划

环上给定 n 个没有包含关系的区间 [li,ri],求必须选第 i 个区间的情况下,还需要选多少区间才能使得 [1,n] 全部被覆盖。

首先断环为链,对于本题,要么处理区间,要么处理位置。

处理区间的话,我们有显然的贪心策略:如果按右端点排序,跳到最后一个左端点小于当前区间右端点的区间。这个可以简单预处理得到。

然后需要从一个区间不断跳,直到跳到这个区间左端点右边,我们有 O(n2) 的模拟,可以倍增优化这个过程。

哈希

通过把某个对象通过对其特征的描述映射为一个值来去重/判断相等。

关键在于抽象其关键信息,以构造适当的哈希函数。

字符串哈希

把字符串看作 B (B>26) 进制数,随机选择一个大质数 P。结论是两个随机字符串的哈希值相同的概率大约是 1P。所以当比较次数较大的时候使用双模哈希。但打CF的时候小心被人对着卡。

自然溢出容易撞的原因是:如果哈希函数构造不当导致很多偶数乘进来,例如结果为一个 2100d,自然溢出的结果为 0

例题7 异或哈希:[ABC250E] Prefix Equality

给定两个序列 A,B,多次询问,每次给出 x,y,判断 A 的前 x 项和 B 的前 y 项构成的集合是否相同。(集合不可重)

显然是和顺序无关的函数,而且我们只在第一次出现的地方修改哈希即可。

常见的套路就是每个值随一个大数,异或起来。

如果拓展到区间,放到主席树上就ok了。

Trie

例题8:P3065 [USACO12DEC] First! G

给定若干字符串,你可以任意规定字符间的偏序关系,求有多少字符串能够成为字典序最小的字符串。

首先只有作为前缀的字符串才有可能是答案。

建出 trie 树,一个串能成为答案,那么从根到这个节点,会形成若干偏序关系,我们需要判断这个偏序关系是否合法。

后者是经典图论模型,偏序关系连边后判断是否成环即可。可以用拓扑排序。

例题9:P4551 最长异或路径

给定一棵树,边有边权,求两点间路径异或和最大是多少。

树上路径异或直接前缀和,甚至不需要求 lca。

问题转换为 n 个数两两异或和最大是多少。都插到 01trie 里,查询的时候尽可能反着走就行。

注意全局加一是从小往大插,其它都是从大往小插。

例题10:CF888G Xor-MST

有一张 n 个点的完全图,边权为 aiaj,求MST。

妙妙题。

实际上是 B开头的 MST 算法,考虑初始有 n 个连通块,每次寻找所有连通块连出去的最小边,将这些边加入最小生成树,并合并这些连通块。

每次连通块个数至少减半,所以复杂度是 O(mlogn)

对于这个题,我们建出 01trie 后,如何找最小边?这两个点的 lca 一定尽可能深,所以 dfs 一遍tire,从下往上,找到一条最小的边,合并每个点的左右子树。根据 B-算法,这样做是正确的。不需要 dsu 等其他东西。

如何找到最小的边?可以遍历较小的一棵子树,在另一棵子树查询,复杂度类似启发式合并。

也可将原数组排序后再建树,这样树上每个点都对应原数组一段区间。直接遍历即可,每个点只会被其祖先访问,01trie深度是对数级别的。总复杂度 O(nlog2n)

posted @   Apricity8211  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示