求割点/割边/强连通分量/双连通分量/LCA(最近公共祖先)

文章参考

割点:如果在图G中去掉一个顶点(自然同时去掉与该顶点相关联的所有边)后,该图的连通分支数增加,则称该顶点为G的割点(cut-vertex)。

 

/* memset(vis,0,sizeof(vis));*/
void tarjan(int x)
{
    vis[x]=1;
    dfn[x]=low[x]=++Time;
    for(int i=first[i]; i!=-1; i=next[i])
    {
        if(!vis[v[i]])
        {
            tarjan(v[i]);
            low[x] = min(low[x], low[v[i]]);
            if(dfn[x]<=low[v[i]]) vis[x]++;
        }
        else 
            low[x] = min(low[x], dfn[v[i]]);
    }
    if( x==root&&vis[x]>2 || x!=root&&vis[x]>1)
        vis[x]=2;  //x是割点
    else  
        vis[x]=1;  //x不是割点
}

割边(桥):假设有连通图G,e是其中一条边,如果G-e是不连通的,则边e是图G的一条 割边

/*
  memset(vis,0,sizeof(vis));
  memset(fa,0,sizeof(fa));
  memset(judge,0,sizeof(judge));
*/
void tarjan(int x)
{
    vis[x]=1;
    dfn[x]=low[x]=++Time;
    for(int i=first[i]; i!=-1; i=next[i])
    {
        if(!vis[v[i]])
        {
            fa[v[i]]=edge[i];
            tarjan(v[i]);
            low[x] = min(low[x],low[v[i]]);
        }
        else 
            if(fa[x]!=edge[i]&&low[x]>dfn[v[i]])
            low[x]=dfn[v[i]];
    }
    if(fa[x]&&low[x]==dfn[x]) 
        judge[fa[x]]=true;
}
强连通分量:有向图强连通分量在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向 路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通 分量(strongly connected components)。

/*
  stack<int>st;
  memset(vis,0,sizeof(vis));
  memset(inst,0,sizeof(inst));
*/
void tarjan(int x)
{
    vis[x]=1;
    dfn[x]=low[x]=++Time;
    inst[x]=1;
    st.push(x);
    for(int i=first[x];i!=-1;i=next[i])
    {
        if(!vis[v[i]])
        {
            tarjan(v[i]);
            low[x]=min(low[x],low[v[i]]);
        }
        else if(in[v[i]]) 
            low[x]=min(low[x],dfn[v[i]]);
    }
    if(low[x]==dfn[x])
    {
        num++;
        do{
            j=st.top();
            st.pop();
            scc[j]=num;
            in[j]=0;
        }
        while(j!=x);
    }
}


点双连通分量:在无向连通图中,如果删除该图的任何一个结点都不能改变该图的连通性,则该图为双连通的无向图。一个连通的无向图是双连通的,当且仅当它没有关键点。

/*memset(in,0,sizeof(in));
  memset(vis,0,sizeof(vis));
*/
void tarjan(int x, int pre)
{
    low[x]=dfn[x]=++tim;
    vis[x]=1;
    in[x]=1;
    st.push(x);
    for(int i=first[x];i;i=next[i])
    {
        if(!vis[v[i]])
        {
            tarjan(v[i],x);
            low[x]=min(low[x],low[v[i]]);
            if(dfn[x]<=low[v[i]])
            {
                int j;
                num++; cnt=0;
                do{
                    j=st.top(); st.pop();
                    cir[++cnt]=j;
                    in[j]=0;
                    belong[j]=num;
                } while(j!=v[i]);
                cir++[cnt]=x;
            }
        }
        else if(v[i]!=pre&&in[v[i]])
            low[x]=min(low[x],dfn[v[i]]);
    }
}


LCA(最近公共祖先):对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。

/*
int found(int x)
{
    while(fa[x]!=x) x=fa[x];
    return x;
}
  memset(vis,0,sizeof(vis));
  memset(r,0,sizeof(r));
  for(int i=1;i<=n;i++)dis[i]=INF;//调用tarjan(a)时,disa=0;
*/
void tarjan(int x)
{
    fa[x]=x;
    vis[x]=1;
    for(int i=first2[x];i!=-1;i=next2[i])
    {
        if(vis[v2[i]] && r[belong[i]]==0)
            tem=found(v2[i]),
            ans[belong[i]]=dis[x]+dis[v2[i]]-(dis[tem]<<1);
    }
    for(int i=first1[x];i!=-1;i=next1[i])
    {
        if(!vis[v1[i]])
        {
            dis[v1[i]]=dis[x]+w[i];
            tarjan(v1[i]);
            fa[v1[i]]=x;
        }
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值