Tarjan系列1

tajan的dfs树系列算法:

求解割点,桥,强连通分量,点双联通分量,边双联通分量;

tajan是一个dfs,把一个图变成一个dfs树结构,

dfs树结构,本质是通过一个没有任何要求的dfs把图的边分为:树边和返祖边:

  • 树边:dfs中父节点与其未曾遍历过的子节点间的边,
  • 返祖边:父节点与他的dfs中曾作为该父节点祖先的子节点间的边

在有向图中,除了这二种边外,还有父节点与曾遍历过的子节点间的边,然而这个子节点不是父节点的祖先,

然而这种边在tarjan中没有意义,我们所求的东西用不上她们

伪代码:

深搜(点now){

  更新点now——

    dfs序(dfn)与目前可到dfn最小祖先的dfn(low),标记已经遍历(vis),确认now将是他后继递归的点的祖先(instk),其他

  for(以now为起点的所有边)

    if(边终点to未遍历)

      深搜(to),low[now]=min(low[now],low[to])

    else

      if(instk[to]为真)//无向图可以不存在这个

        low[now]=min(low[now],dfn[to])

  更新instk[now]为假

}

求(无向图)割点,桥:

割点:无向图中,删除之可改变图的连通性的点

  两种:

  • 两个点双连通分量的公共点
  • 两个点双连通分量直接存在一条边连接,这条边的两端点

桥:无向图中,删除之可改变图的连通性的边

  一种:

  两个边双连通分量间的连边;

求法:

割点:这个点儿子中有至少一个的low小于这个点的dfn

桥:该边终点的low=dfn

例题:

hihocoder1183连通性一·割边与割点

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
struct ss{
    int to,next;
}e[200010];
struct Cl{
    int u,v;
}Cedge[200010];
int first[20010],num;
int pnu,enu;
int Cpoint[20010];
int dfn[20010],low[20010],vis[20010];
bool cmp(Cl a,Cl b){
    return a.u<b.u||(a.u==b.u&&a.v<b.v);
}
void Input();
void work();
void Output();
void build(int ,int );
void Init();
void dfs_1(int ,int );
void dfs_2(int ,int );
int main()
{
    Input();
    work();
    Output();
    return 0;
}
void Input(){
    int i,j,k;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&j,&k);
        build(j,k);build(k,j);
    }
}
void work(){
    pnu=0;enu=0;
    Init();
    dfs_1(1,0);
    Init();
    dfs_2(1,0);
}
void Output(){
    int i;
    for(i=1;i<=enu;i++)
        if(Cedge[i].u>Cedge[i].v)
            swap(Cedge[i].u,Cedge[i].v);
    sort(Cpoint+1,Cpoint+pnu+1);
    sort(Cedge+1,Cedge+enu+1,cmp);
    for(i=1;i<=pnu;i++)
        printf("%d ",Cpoint[i]);
    if(pnu==0)
        printf("Null");
    printf("
");
    for(i=1;i<=enu;i++)
        printf("%d %d
",Cedge[i].u,Cedge[i].v);
}
void build(int f,int t){
    e[++num].next=first[f];
    e[num].to=t;
    first[f]=num;
}
void Init(){
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    num=0;
}
void dfs_1(int now,int fa){
    int i,p=0;
    if(now!=1)p=1;
    dfn[now]=low[now]=++num;vis[now]=1;
    for(i=first[now];i;i=e[i].next)
        if(e[i].to!=fa){
            if(!vis[e[i].to]){
                dfs_1(e[i].to,now);
                if(low[e[i].to]>=dfn[now])p++;
                if(low[now]>low[e[i].to])
                    low[now]=low[e[i].to];
            }
            else
                if(low[now]>dfn[e[i].to])
                    low[now]=dfn[e[i].to];
        }
    if(p>=2)
        Cpoint[++pnu]=now;
}
void dfs_2(int now,int fa){
    int i;
    dfn[now]=low[now]=++num;vis[now]=1;
    for(i=first[now];i;i=e[i].next)
        if(e[i].to!=fa){
            if(!vis[e[i].to]){
                dfs_2(e[i].to,now);
                if(low[now]>low[e[i].to])
                    low[now]=low[e[i].to];
                if(low[e[i].to]>dfn[now])
                    Cedge[++enu].u=now,Cedge[enu].v=e[i].to;
            }
            else
                if(low[now]>dfn[e[i].to])
                    low[now]=dfn[e[i].to];
        }
}
View Code

求(有向图)强连通分量:

强联通分量:有向图中,点的可以相互到达的关系可以传递,于是有这个关系的一组点与其间的边构成一个强连通分量

求法:

每遍历一个点时,使之进栈,

当遍历结束时,

若其low=dfn,则从栈顶到该节点的所有点属于同一分量,且该分量不含其它点,标记她们,并使她们出栈

(now永远不能到达上层的点,于是她与她的子树中没有自成一派的点构成强连通分量,自成一派的点已经出栈了)

例题:

bzoj1051[HAOI2006]受欢迎的牛

强连通分量缩点后讨论无出度点的个数

code:

#include<cstdio>
using namespace std;
struct ss{
    int to,next;
}e[2000010];
int first[200010],num;
int dfn[100010],low[100010],vis[200010],stk[100010],col[100010],number;
int numcol[200010],into[100],color;
int max[200010];
long long f[200010],x;
int n,m;
void Input();
void work();
void Output();
void build(int ,int );
void tar(int );
void con_poi();
void dfs(int );
int main()
{
    Input();
    work();
    Output();
    return 0;
}
void Input(){
    int i,j,k;
    scanf("%d%d%lld",&n,&m,&x);
    for(i=1;i<=m;i++){
        scanf("%d%d",&j,&k);
        build(j,k);
    }
}
void work(){
    int i,j;
    color=n;
    for(i=1;i<=n;i++)
        if(!vis[i])
            number=0,tar(i);
    con_poi();
    for(i=n+1;i<=color;i++)
        if(!vis[i])
            dfs(i);
}
void Output(){
    int i;
    long long ans=0,ans_=0;
    for(i=n+1;i<=color;i++){
        if(max[i]==ans)
            (ans_+=f[i])%=x;
        if(max[i]>ans)
            ans=max[i],ans_=f[i];
    }
    printf("%lld
%lld
",ans,ans_);
}
void build(int f,int t){
    e[++num].next=first[f];
    e[num].to=t;
    first[f]=num;
}
void tar(int now){
    int i;
    dfn[now]=low[now]=++number;
    vis[now]=2;stk[++stk[0]]=now;
    for(i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]){
            tar(e[i].to);
            if(low[now]>low[e[i].to])
                low[now]=low[e[i].to];
        }
        else
            if(vis[e[i].to]==2&&low[now]>dfn[e[i].to])
                low[now]=dfn[e[i].to];
    if(dfn[now]==low[now]){
        ++color;
        while(stk[stk[0]+1]!=now){
            col[stk[stk[0]]]=color;
            vis[stk[stk[0]]]=1;
            ++numcol[color];
            --stk[0];
        }
        number=stk[0];
    }
}
void con_poi(){
    int i,j;
    for(i=1;i<=n;i++)
        for(j=first[i];j;j=e[j].next)
            if(col[i]!=col[e[j].to])
                build(col[i],col[e[j].to]),into[col[e[j].to]]++;
}
void dfs(int now){
    int i;
    f[now]=1;max[now]=numcol[now];
    for(i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]){
            vis[e[i].to]=1;
            if(!f[e[i].to])
                dfs(e[i].to);
            if(max[now]==max[e[i].to]+numcol[now])
                (f[now]+=f[e[i].to])%=x;
            if(max[now]<max[e[i].to]+numcol[now]){
                max[now]=max[e[i].to]+numcol[now];
                f[now]=f[e[i].to];
            }
        }
    for(i=first[now];i;i=e[i].next)
        if(vis[e[i].to])
            vis[e[i].to]=0;
}
View Code

bzoj1093[ZJOI2007]最大半连通子图

强连通分量缩点,计算最长路和最长路计数

code:

#include<cstdio>
using namespace std;
struct ss{
    int to,next;
}e[2000010];
int first[200010],num;
int dfn[100010],low[100010],vis[200010],stk[100010],col[100010],number;
int numcol[200010],into[100],color;
int max[200010];
long long f[200010],x;
int n,m;
void Input();
void work();
void Output();
void build(int ,int );
void tar(int );
void con_poi();
void dfs(int );
int main()
{
    Input();
    work();
    Output();
    return 0;
}
void Input(){
    int i,j,k;
    scanf("%d%d%lld",&n,&m,&x);
    for(i=1;i<=m;i++){
        scanf("%d%d",&j,&k);
        build(j,k);
    }
}
void work(){
    int i,j;
    color=n;
    for(i=1;i<=n;i++)
        if(!vis[i])
            number=0,tar(i);
    con_poi();
    for(i=n+1;i<=color;i++)
        if(!vis[i])
            dfs(i);
}
void Output(){
    int i;
    long long ans=0,ans_=0;
    for(i=n+1;i<=color;i++){
        if(max[i]==ans)
            ans_+=f[i];
        if(max[i]>ans)
            ans=max[i],ans_=f[i];
    }
    printf("%lld
%lld
",ans,ans_);
}
void build(int f,int t){
    e[++num].next=first[f];
    e[num].to=t;
    first[f]=num;
}
void tar(int now){
    int i;
    dfn[now]=low[now]=++number;
    vis[now]=2;stk[++stk[0]]=now;
    for(i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]){
            tar(e[i].to);
            if(low[now]>low[e[i].to])
                low[now]=low[e[i].to];
        }
        else
            if(vis[e[i].to]==2&&low[now]>dfn[e[i].to])
                low[now]=dfn[e[i].to];
    if(dfn[now]==low[now]){
        ++color;
        while(stk[stk[0]+1]!=now){
            col[stk[stk[0]]]=color;
            vis[stk[stk[0]]]=1;
            ++numcol[color];
            --stk[0];
        }
        number=stk[0];
    }
}
void con_poi(){
    int i,j;
    for(i=1;i<=n;i++)
        for(j=first[i];j;j=e[j].next)
            if(col[i]!=col[e[j].to])
                build(col[i],col[e[j].to]),into[col[e[j].to]]++;
}
void dfs(int now){
    int i;
    f[now]=1;max[now]=numcol[now];
    for(i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]){
            vis[e[i].to]=1;
            if(!f[e[i].to])
                dfs(e[i].to);
            if(max[now]==max[e[i].to]+numcol[now])
                (f[now]+=f[e[i].to])%=x;
            if(max[now]<max[e[i].to]+numcol[now]){
                max[now]=max[e[i].to]+numcol[now];
                f[now]=f[e[i].to];
            }
        }
    for(i=first[now];i;i=e[i].next)
        if(vis[e[i].to])
            vis[e[i].to]=0;
}
View Code

bzoj2438[中山市选2011]杀人游戏

强连通分量缩点,乱搞,具体看代码;

code:

#include<cstdio>
#include<cstring>
using namespace std;
int n,m,p,ans;
struct ss{
    int to,next;
}e[600010];
int first[200010],num;
int dfn[100010],low[100010],vis[200010],stk[200010],number;
int col[100010],color,numcol[200010];
int toit[200010];
void Input();
void work();
void Output();
void build(int ,int );
void tar(int );
void dfs_1(int );
int main()
{
    Input();
    work();
    Output();
    return 0;
}
void Input(){
    int i,j,k;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++){
        scanf("%d%d",&j,&k);
        build(j,k);
    }
}
void work(){
    int i,j,k;
    color=n;
    for(i=1;i<=n;i++)
        if(!vis[i])
            number=0,tar(i);
    for(i=1;i<=n;i++)
        for(j=first[i];j;j=e[j].next)
            if(col[i]!=col[e[j].to])
                build(col[i],col[e[j].to]),toit[col[e[j].to]]++;
    memset(stk,0,sizeof(stk));
    for(i=n+1;i<=color;i++)
        if(!toit[i])
            vis[i]++,dfs_1(i);
    for(i=n+1;i<=color;i++)
        if(!toit[i]){
            k=(numcol[i]==1);
            for(j=first[i];j;j=e[j].next)
                k&=(vis[e[j].to]>1);
            p|=k;
            ans++;
        }
}
void Output(){
    printf("%.6lf",1.0-(double)(ans-p)/(double)(n));
}
void build(int f,int t){
    e[++num].next=first[f];
    e[num].to=t;
    first[f]=num;
}
void tar(int now){
    int i;
    dfn[now]=low[now]=++number;
    stk[++stk[0]]=now;vis[now]=2;
    for(i=first[now];i;i=e[i].next)
        if(!vis[e[i].to]){
            tar(e[i].to);
            if(low[now]>low[e[i].to])
                low[now]=low[e[i].to];
        }
        else
            if(vis[e[i].to]==2&&low[now]>dfn[e[i].to])
                low[now]=dfn[e[i].to];
    if(dfn[now]==low[now]){
        ++color;
        while(stk[stk[0]+1]!=now){
            col[stk[stk[0]]]=color;
            vis[stk[stk[0]]]=1;
            ++numcol[color];
            --stk[0];
        }
        number=stk[0];
    }
}
void dfs_1(int now){
    int i;
    for(i=first[now];i;i=e[i].next)
        if(!stk[e[i].to]){
            stk[e[i].to]=1;
            vis[e[i].to]++;
            if(vis[e[i].to]==1)
                dfs_1(e[i].to);
        }
    for(i=first[now];i;i=e[i].next)
        if(stk[e[i].to])
            stk[e[i].to]=0;
}
View Code

边双点双下次再说吧;

原文地址:https://www.cnblogs.com/nietzsche-oier/p/7522401.html