[学习笔记]优化基础技巧

只能说确实挺基础。
集合起来写写吧。

二分系列

二分

略。

三分

对于传统的定义域在实数上的凸函数求最值,可以使用三分法。

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))\)

那么我们的目标为判断有一条斜率时,如何求出切点位置。

image

观察可得,在上凸包时,其切的点一定为在\(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
}

操作分块

当信息合并复杂度较高的时候,可以考虑定期合并,并且额外考虑零散元素的贡献。

「APIO2019」桥梁 操作分块+带权并查集 LOJ3145

posted @ 2021-12-13 13:07  fhq_treap  阅读(104)  评论(0编辑  收藏  举报