LCA

啊啊啊,最近成为机房神犇HBQ的粉丝,跟着他的脚步学算法
LCA

最近公共祖先,晚上资料无数。我就不贴定义了

先来一个实验田

小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上。有一天,他们想爬到一个节点上去搞基,但是作为两只虫子,他们不想花费太多精力。已知从某个节点爬到其父亲节点要花费 c 的能量(从父亲节点爬到此节点也相同),他们想找出一条花费精力最短的路,以使得搞基的时候精力旺盛,他们找到你要你设计一个程序来找到这条路,要求你告诉他们最少需要花费多少精力

首先自然是我1年前YY出来的最朴素的LCA,太懒了,一直就用了这个,结果错过了好多精妙的技巧,如果能早点深究,省选T1就不会认为是splay而是直接LCT走起啊。
不过也过去了。

int lca(int x,int y)
{
	if(deep[x] < deep[y]) swap(x,y);
	while(deep[x] > deep[y]){
		x = pa[x];
	}
	if(x == y) return x;
	while(x != y){
		x = pa[x];
		y = pa[y];
	}
	return x;
}

简单粗暴,先提到相同深度,然后一起往上跳,跳到两个一样就可以了。

然后,你发现了吧,这个和ST算法一样可以倍增优化。
储存当前节点的第\(2^j\)祖先,就能把寻LCA变成\(O(log n)\)
比想象中容易多了。。。。

int lca(int x,int y)
{
	if(deep[x] < deep[y]) swap(x,y);
	int i;
	for(i = 0;(1<<i) <= deep[x];i++);i--;
	for(int j = i;j >= 0;j--)
		if(deep[x] -(1<<j) >= deep[y])
			x = pa[x][j];
	if(x == y) return x;
	for(int j = i;j >= 0;j--)
		if(pa[x][j] != pa[y][j])
			x = pa[x][j],y = pa[y][j];
	return pa[x][0];
}

void make_fa()
{
	for(int j = 1;(1<<j) < n;j++)
		for(int i = 0;i < n;i++)
			pa[i][j] = pa[pa[i][j-1]][j-1];
}

把pa数组开成二维,然后用ST类似的倍增玩一玩,最后查找还是先提到相同的深度。然后一起往上跳,跳到LCA下一层,最后返回LCA就可以了。
比树剖容易不知道多少,我还欠这么久。。。

节操依旧还在的我觉得再补一个树剖版的来完结会跟好点。
树剖版的估计也很简单,不过代码长点。
如果\(x\)\(y\)在一条链上,直接返回浅的,否则往一条链上靠就好了。。查都不要查,每次直接跳到链首就可以了,一下复杂度一个是\(O(log n)\)取决于两点见差了多少条链。

。。。。 太水了。
这么说,我也是时候补动态规划和AC自动机了。

posted @ 2017-07-18 12:56  rsqppp  阅读(106)  评论(0)    收藏  举报