[学习笔记]优化基础技巧
只能说确实挺基础。
集合起来写写吧。
二分系列
二分
略。
三分
对于传统的定义域在实数上的凸函数求最值,可以使用三分法。
lmid = l + ((r - l) >> 1);
rmid = lmid + ((r - l) >> 1);
if(cal(limd) > cal(rmid))
r = rmid;
else
l = lmid;
对于凸函数的二分
OI里凸函数大多为横坐标差相等的离散函数,我们知道凸函数的导数有单调性,又知OI里是离散的,所以我们直接
二分位置,求\(\Delta\to 0\)的位置即最值位置。
分数规划
一般来说分数规划求如下问题
若干物品有两个权值\(a,b\),选出若干物品使得\(\frac{\sum a}{\sum b}\)最小/最大。
同时可能会有一些奇怪的限制,比如“分母至少为\(W\)”,我会在这个学习笔记配套的练习里说。
我们对该问题采取二分法。
二分答案后我们转为\(check\)。
求\(\frac{\sum a}{\sum b} > mid\)
即可知\(\sum a - mid * \sum b > 0\)
即\(\sum a - mid * b > 0\)
求出左侧柿子最大值即可。
凸优化
一般的问题为强制选出\(k\)个,求最优方案。
我们设\(g(i)\)为\(i\)个物品的最优方案。
然后我们需要证明或大胆猜想:\((i,g(i))\)有凸性。
既然有凸性,依旧根据导数那套理论,其每个点的切线斜率有单调性,那么我们可以二分斜率\(k\),并根据其所对应的点的横坐标调整斜率最终使其切到\((m,g(m))\)。
那么我们的目标为判断有一条斜率时,如何求出切点位置。
观察可得,在上凸包时,其切的点一定为在\(y\)轴上截距最大。
不妨设\(f\)为截距。
则一条直线表现为\(y - f = kx\)
代入点\((i,g(i))\),则有\(g(i) - ki = y \to \sum (a_i - k) = y\)
即给每个点权值减去\(k\),求最大的情况下选取的横坐标,此时\(g(i) = ki + y\),并以此调整。
在二分时有整数域和实数域差别,在练习时说明。
分治系列
基于时间顺序的分治
如果要求计算分治中心左右两部分信息的合并,这两部分求值的先后顺序并没有要求。如果要求计算较早操作对较晚操作的影响,那么可以通过处理左子树的信息,来计算左子树对右子树的贡献。
其思想类似于操作分块,都是考虑操作的并是否能用简单的信息代替。
具体细节见[学习笔记]CDQ分治。
线段树分治
处理如下这类问题:
- 修改具有时效性,即只对\([l,r]\)时间内查询有效果
- 查询某个时间的答案
考虑把询问看做线段树叶子节点,则一个修改等同在线段树上打永久化标记,然后查询等同于该叶子节点到根节点的操作的并,我们直接使用可回退数据结构在树上\(dfs\),则其空间复杂度\(O(nlogn)\),时间复杂度为\(O(nq() + np())\),其中\(q()\)表示为插入一个操作的复杂度,\(p()\)为撤销一个操作的复杂度。
值得注意的是可以使用清除效率高的数据结构,我们直接暴力遍历每条链。
其复杂度更改为\(O(nlognq() + nclear())\),\(clear()\)为清空所有操作的复杂度。
基于分治中心性质的写法
如果分治中遵循先递归左儿子,再递归右儿子,用一个指针去跟踪分治中心,则该指针移动的总距离为\(O(nlogn)\)。
整体二分
考虑一类能二分答案的问题,可以将所有询问都一起二分,然后判断其分治中心的单调性,然后再分开询问接着二分。
一个询问只会在分治树上每一层出现一次,其复杂度为\(O(QlogV)\).
但考虑一类问题,如果无法快速单点求值,但其相邻处非常求,可以利用好分治中心移动的距离不大的性质。(一个经典问题是背包问题,加入一个物品或减去一个物品)。
分治转移斜率优化
实际上就是整体二分转移点,因为转移点单调,所以有简单的写法。
有经典例题在练习记录中给出。
树分治
淀粉质。
变粉质。
淀粉树。
动态淀粉树。
还是到时候另外写一篇博客好了。
启发式合并
基础 和 树上轻重启发式合并
略过不讲。
长链剖分 即 深度启发式合并
按深度合并。
主要来证明一下复杂度\(O(n)\)。
一个点单独被操作时,其一定是在重链顶端。
值得注意的是,其因为被合并时其深度遍历集合一定被重儿子里的深度集合覆盖,所以确保了复杂度。
所以就是\(O(n)\)。
倍增
可以处理多查询,信息可合并的题目。
扫描线
典中典,略。
分块
数论分块
即对求\(sum_{i = 1}^b \lfloor\frac{n}{i}\rfloor * f(i)\)
对\(f(i)\)做前缀和,\(\lfloor\frac{n}{i}\rfloor\)只有根号个区间,我们根号个一起处理。
其中\(\lfloor\frac{n}{i}\rfloor = \lfloor\frac{n}{l}\rfloor\)的最大右端为\(\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor\)。
int ans = 0;
for(int l = 1, r = 0; l <= n; l++) {
r = n / (n / l);
// do something
}
操作分块
当信息合并复杂度较高的时候,可以考虑定期合并,并且额外考虑零散元素的贡献。