有向图的强连通分量,缩点a!
无向图求割点a! 求桥a!
求lca!
复杂度是O(n+m)
有向图的强连通分量板子
void tarjian(int x)
{
dfn[x] = low[x] = ++idc;
vis[x] = 1;
zhan[++top] = x;
for(int i = h[x]; i; i = nex[i])
{
int v = tov[i];
if(dfn[v] == 0)
{
tarjian(v);
low[x] = min(low[x],low[v]);
}
else if(vis[v] == 1)
{
low[x] = min(low[x],dfn[v]);
}
}
if(dfn[x] == low[x])
{
seq++;
while(1)
{
int u = zhan[top];
top--;
tow1[seq] += tow[u];
color[u] = seq;
vis[u] = 0;
if(u == x) break;
}
}
}
for(int i = 1; i <= m; i++)
{
if(color[ha[i]] != color[he[i]])
{
add(color[ha[i]],color[he[i]]);
}
}//缩点
void tarjian(int x,int fa)//割点
{
int child = 0;
dfn[x] = low[x] = ++idc;
for(int i = h[x]; i ;i = nex[i])
{
int v = tov[i];
if(v == fa) continue;
if(dfn[v] == 0)
{
child++;
tarjian(v,x);
if(low[v] >= dfn[x] &&fa != -1)
{
if(vis1[x] == 0) {
vis1[x] = 1;
cnt++;
}
}
low[x] = min(low[v],low[x]);
}
else
{
low[x] = min(dfn[v],low[x]);
}
}
if(fa == -1 && child >= 2)
{
if(vis1[x] == 0)
{vis1[x] = 1;
cnt++;}
}
}//只要有一个儿子无法回到它的祖先,它就是割点,根节点有两个及以上儿子则是
桥
void tarjian(int x,int fa)//无重边
{
int child = 0;
dfn[x] = low[x] = ++idc;
for(int i = h[x]; i ;i = nex[i])
{
int v = tov[i];
if(v == fa) continue;
if(dfn[v] == 0)
{
child++;
tarjian(v,x);
if(low[v] > dfn[x])
{
vis1[i] = 1;
cnt++;
}
low[x] = min(low[v],low[x]);
}
else
{
low[x] = min(dfn[v],low[x]);
}
}
}//只要有一个儿子无法回到它的祖先和它,则这条边是桥 ,此为无重边=、=
//然而现实是残酷的,往往有重边,它可以通过除了它来的这条边之外的边回到父亲,所以不能只要v == fa
//就回去=、=
void tarjian(int x,int id)//有重边
{
int child = 0;
dfn[x] = low[x] = ++idc;
for(int i = h[x]; i ;i = nex[i])
{
if(i == id/2) continue;
int v = tov[i];
if(dfn[v] == 0)
{
child++;
tarjian(v,i);
if(low[v] > dfn[x])
{
vis1[i] = 1;
cnt++;
}
low[x] = min(low[v],low[x]);
}
else
{
low[x] = min(dfn[v],low[x]);
}
}
}//所以记它是从哪条边来的,只要 i == id/2说明这和来的边是一条边=。=、
求lca
一种离线算法
对于询问(u, v)
if v在u的子树中那么lca为u
if v不在u的子树中那么lca为u
v有可能在u爸爸的子树中,如果不在,v有可能在u爸爸的爸爸的子树中
//类似于并查集
#include<iostream>//这是 P3379 【模板】最近公共祖先(LCA)
#include<cstdio>
using namespace std;
int nex[1000005], tp, h[1000005], tov[1000005];
int tpp,nexx[1000005],hh[1000005],tovv[1000005];
int n,m,s,fa[1000005],tow[1000005],ans[1000005];
void add(int x,int y)
{
tp++;
nex[tp] = h[x];
h[x] = tp;
tov[tp] = y;
}
void add1(int x,int y,int w)
{
tpp++;
nexx[tpp] = hh[x];
hh[x] = tpp;
tovv[tpp] = y;
tow[tpp] = w;
}
int find(int x)
{
if(x == fa[x]) return x;
return fa[x] = find(fa[x]);
}
void tarjian(int x)
{
fa[x] = x;//假设处理以x为根的一棵小子树 ;
for(int i = h[x]; i ; i = nex[i])
{
int v = tov[i];
if(fa[v] == 0)
{
tarjian(v);
fa[v] = x;
}
}
for(int i = hh[x]; i ; i = nexx[i])
{
int v = tovv[i];
if(fa[v] != 0&&ans[tow[i]] == 0)
{
ans[tow[i]] = find(v);//u = Lca(x,v)如果u = fa[u],说明 x,v在 u的子树中
//从v一直往上找,找到的第一个 u(满足u = fa[u]) ,说明 u = Lca(x,v)
}
}
}
int main()
{
cin >> n >> m >> s;
for(int i = 1; i <= n - 1; i++)
{
int x,y;
scanf("%d%d", &x,&y);
add(x,y);
add(y,x);
}
for(int i = 1; i <= m; i++)
{
int x,y;
scanf("%d%d", &x,&y);
add1(x,y,i);
add1(y,x,i);
}
tarjian(s);
for(int i= 1; i <= m; i++)
{
cout << ans[i] << endl;
}
return 0;
}