jQuery火箭图标返回顶部代码 - 站长素材
jQuery火箭图标返回顶部代码 - 站长素材

关于倍增

关于倍增

学习博客

前几天\(lyk\)给我们讲了倍增但是\(emmmm\)他说话声音也太小了,坐在后排根本听不清楚诶,前边一群大佬还在叽叽喳喳的说话,上课效率低得一批。

\[\color{purple}{我太难了} \]

顾名思义倍增就是倍增,用\(dsr\)大佬的话来说就是\(wuwu\)腻。

我们有一个数组\(f[i][j]\)表示从\(i\)开始的长度为\(2^j\)的区间即区间\([i,i+2^j]\)

递推公式:

预处理ST表
\(f[i][j]=max(f[i][j-1]),f[i+2^{j-1}][j-1]\)
说句闲话
为什么这么写?我之前第一遍自己学的时候,看其他人的博客,有的直接不解释,有的说自己揣摩\(qwq\),我就是揣摩不出来才去看您的\(blog\)的哇。
后来\(lyk\)讲课问谁没听懂,偌大的教室里,只有我的小短手在教室后边高高的自信的举起,再不会就对不起\(lyk\)的一对一辅导了.

灵魂画师又要入场了

多么清晰多么明了博主已飘

for(int i = 1; i <= m; i++) f[i][0] = read();//自己到自己这个区间
for(int j = 1; j <= log(m); j++)
	for(int i = 1; i + (1 << j) <= m; i++)
		f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); 

j的循环范围

这里说一下为什么循环外层是\(j\)
再说一句闲话:
\(wtf\)?刚刚我在画图,阿龙过来来了一句:“你又待聂画画,打草纸组啥,光待聂画画。”嗯??你告诉我往草纸上画咋粘博客里。双面胶?来来来,舞台留给你,请开始你的表演。于是乎,\(lkx\)大佬帮我画了张图

回归正题,咱们看图说话并没有什么用的图假设我们先枚举\(i\)那么我们的枚举顺序就是\([1,1],[1,2]......[1,m]\)当我们更新到\([1,2]\)时,就要用到\([1,1]\)\([2,2]\)这两个点,但是由于我们的外层循环是\(i\)此时的\([2,2]\)还没有值,所以这样更新出来的就是错的。

查询:

把我们需要查询的区间\([l,r]\)分成两段\([l,2^k]\)\([r-2^k+1,r]\)其中这个\(k\)\(2^k \leq r-l+1\)的所有数中的最大的数,就算这两部分互相覆盖由于他们是取最值,所以对答案没有影响
\(f[l][r]=max(f[l][k],f[r-(1<<k)+1][k])\)
为什么左端点是\(r-2^k+1\)可能只有我这样的才会问了好久才明白
简单一点我们设\(L\)为他的左端点,因为是一个闭区间且已知区间长度及区间右端点,可得方程\(r-L+1=2^k\)解得L即为左端点

倍增其实还有个很常用的应用:

求LCA!

此时设\(f[i][j]\)表示从节点\(i\)向上跳\(2^j\)个节点
\(f[i][j]=f[f[i][j-1]][j-1]\)

void work(int x, int fa) {
	f[x][0] = fa;
	dep[x] = dep[fa] + 1;
	for(int i = 1; i < 20; i++) 
		f[x][i] = f[f[x][i - 1]][i - 1];
}
void dfs(int x, int fa) {
	work(x, fa);
	for(int i = head[x]; i; i = e[i].nxt) 
		if(e[i].to != fa) work(e[i].to, x);
}

\(dfs\)的过程中更新
一种优化常数的方法:先处理出\(lg\)数组,表示以\(2\)为底\(i\)的对数

for(int i = 2; i <= n; i++)
	lg[i] = lg[i >> 1] + 1;
查询:

先使两个待查询的点跳到同一深度,然后再从\(i\)开始枚举,逼近他们的LCA,最后一定会是在他们的LCA的下一层,最后直接输出当前节点的爸爸也就是\(f[i][0]\)

	if(dep[a] < dep[b]) swap(a, b);
	for(int i = 20; i >= 0; i--) 
		if(dep[f[a][i]] >= dep[b])
			a = f[a][i];//跳到同一深度 
	if(a == b) return a;//或者是b反正他俩都一样
	for(int i = 20; i >= 0; i--)
		if(f[a][i] != f[b][i])
			a = f[a][i], b = f[b][i];//同时跳 
	return f[a][0]; 
}

\(qwq\)貌似是有\(O(1)\)的的查询的喂

我:“\(lyk\),我\(O(1)\)查询的不会”
大佬:“\(hai \ xie\),不用会这锅啊,,谁写这\(hu\)滴啊”

路径最小权值

我们设\(w[i][j]\)表示从节点\(i\)向上跳\(2^j\)个节点内所经过的最小的权值

for(int j = 1; j < 20; j++)
	for(int i = 1; i + (1 << j) - 1 <= tot; i++)
		w[i][j] = min(w[i][j - 1], w[f[i][j - 1]][j - 1]);
//f[i][j]是预先处理处的代表i号点向上跳2^j步之后跳到的节点 

历时一上午多点我总算把这篇文章磨完了........

谢谢收看, 祝身体健康!

posted @ 2019-10-29 14:45  lzpclxf  阅读(363)  评论(0编辑  收藏  举报