文章目录
Tarjan求LCA
用Tarjan求LCA可以用 O ( N l o g N + Q ) O(N log N+Q) O(NlogN+Q)的时间复杂度离线求出 Q Q Q个LCA询问哟!
思路:
我们先对树进行遍历。众所周知,A若是在B的子树里,那A和B的LCA就是B。然后第二种情况,设一个k,A和B在k的子树里,且k是深度最大的,那k就是A和B的LCA。
这是基本的思路,那怎样实现呢?
我们想到了并查集。
每次遍历,用并查集将每个点的父亲记录下来,每次回溯前将要求的LCA遍历(当然vector是更好的选择),通过并查集来找出两点的LCA
c o d e code code
void dfs(int x)
{
f[x]=x;
b[x]=1;
for(int i=head[x]; i; i=e[i].next)
{
int to=e[i].to;
if(!b[to])
dfs(to), f[to]=x;
}
for(int u=1; u<=n; u++)
if(a[x][u]&&b[u])
lca[x][u]=find_fa(x);
return;
}
Tarjan求割点、割边
割点就是一个连通图中把它和它相连的边删掉以后变得不连通的点。
至于用Tarjan求割点,和Tarjan缩点的思路相似。
c o d e code code
void tarjan(int x)
{
fn[x]=low[x]=++cnt;
v[x]=1;
int child=0;
for(int i=head[x]; i; i=b[i].next)
{
int y=b[i].to;
if(!dfn[y])
{
if(dfn[x]==1)
{
child++;
if(child>=2)
key[x]=1;
}
tarjan(y);
low[x]=min(low[x], low[y]);
if(dfn[x]<=low[y]&&dfn[x]>1)
key[x]=1;
}
else
low[x]=min(low[x], dfn[y]);
}
}
至于求割边(定义差不多),思路和代码基本完全一致
c o d e code code
void tarjan(int x)
{
fn[x]=low[x]=++cnt;
v[x]=1;
int child=0;
for(int i=head[x]; i; i=b[i].next)
{
int y=b[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x], low[y]);
if(dfn[x]<low[y])
key[x][y]=1;
}
else
low[x]=min(low[x], dfn[y]);
}
}
Tarjan求强连通分量
总之就是那个缩点
c o d e code code
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
v[x]=1;
stack[++top]=x;
for(int i=head[x]; i; i=b[i].next)
{
int y=b[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x], low[y]);
}
else if(v[y])
low[x]=min(low[x], dfn[y]);
}
if(dfn[x]==low[x])
{
tmp++;
do
{
c[stack[top]]=tmp;
//cout<<c[stack[top]]<<' '<<stack[top]<<endl;
d[tmp]+=a[stack[top]];
v[stack[top]]=0;
top--;
}
while(x!=stack[top+1]);
}
return;
}
总结
又水了一篇……