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自动机了。

浙公网安备 33010602011771号