缩点+割点(tarjan)

1.tarjan缩点

思路:找到图中的强联通分量,染成同色,形成DAG(有向无环图).

vis:记录点是否在栈中.

dfn:点在dfs中顺序,即时间戳.

col:同一强连通分量的染色颜色.

low:点与点的子辈能回到的最小时间戳.

其实也可以有其他辅助数组,但核心的数组就这四个.

难点:1.low[x]=min(low[x],low[to]),其实就是不断用子辈low更新,定义可知.

2.dfn[x]=low[x]进行染色操作,用子辈更新low完后回溯发现dfn[x]=low[x],x为强连通分量的根.

以lg3387缩点为例

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define R register
#define e exit(0)
const int maxn=1e5+10;
int n,m,vd=-1,cnt,sum,top,deep,vis[maxn],val[maxn],head[maxn],dfn[maxn],ans[maxn];
int cnt1,head1[maxn],low[maxn],stact[maxn],col[maxn],check[1010][1010],cd[maxn];
struct bian{
    int to,next;
}len[maxn];
struct bian1{
    int to,next;
}len1[maxn];
inline int fd()
{
    int s=1,t=0;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            s=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        t=t*10+c-'0';
        c=getchar();
    }
    return s*t;
}
void add(int from,int to)
{
    len[++cnt].to=to;
    len[cnt].next=head[from];
    head[from]=cnt;
}
void add1(int from,int to)
{
    len1[++cnt1].to=to;
    len1[cnt1].next=head1[from];
    head1[from]=cnt1;
}
void tarjan(int x)
{
    dfn[x]=++deep;
    low[x]=deep;
    stact[++top]=x;
    vis[x]=1;
    for(R int k=head[x];k;k=len[k].next)
    {
        int to=len[k].to;
        if(!dfn[to])
        {
            tarjan(to);
            low[x]=min(low[x],low[to]);
        }
        else if(vis[to])
            low[x]=min(low[x],low[to]);
    }
    if(dfn[x]==low[x])
    {
        int va=0;
        col[x]=++sum;
        vis[x]=0;
        while(stact[top]!=x)
        {
            va+=val[stact[top]];
            col[stact[top]]=sum;
            vis[stact[top--]]=0;
        }
        va+=val[stact[top]];
        ans[sum]=va;
        --top;
    }
}
void dfs(int x,int sumv)
{
    vd=max(sumv,vd);
    for(R int k=head1[x];k;k=len1[k].next)
    {
        int to=len1[k].to;
        int nowsum=sumv+ans[to];
        dfs(to,nowsum);
    }
}
int main()
{
    //freopen("s.in","r",stdin);
    //freopen("s.out","w",stdout);
    n=fd(),m=fd();
    memset(check,0,sizeof(check));
    for(R int i=1;i<=n;++i)
        val[i]=fd();
    for(R int i=1;i<=m;++i)
    {
        int from=fd(),to=fd();
        add(from,to);
    }
    for(R int i=1;i<=n;++i)
        if(!dfn[i])
            tarjan(i);
    if(sum==1)
    {
        printf("%d",ans[1]);
        return 0;
    }
    for(R int i=1;i<=n;++i)
        for(R int k=head[i];k;k=len[k].next)
        {
            int to=len[k].to;
            if(col[i]!=col[to])
            {
                int num1=col[i],num2=col[to];
                if(!check[num1][num2])
                {
                    check[num1][num2]=1;
                    add1(num1,num2);
                    cd[num1]=1;
                }
            }
        }
    for(R int i=1;i<=n;++i)
        if(cd[i])
            dfs(i,ans[i]);
    printf("%d",vd);
    return 0;
}

2.tarjan求割点.

为割点:1.发现子结点并不能通过其他路径到达比祖先深度更浅的节点,祖先为割点.

2.为为枚举的根节点,并且发现其有两个即以上的儿子,这个根节点为割点.

low跟新:low[x]=min(low[x],low[to]),与上面一致.

第二处:if(to!=fa&&dfn[to]<dfn[x]) low[x]=min(low[x],dfn[to]),在各大博客未找到证明.

以lg3388为例:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
using namespace std;
#define R register
const int maxn=1e5+10;
int n,m,cnt,ans,deep,lenth,head[maxn],dfn[maxn],low[maxn],cut[maxn],q[maxn];
struct bian{
    int to,next;
}len[maxn<<1];
inline int fd()
{
    int s=1,t=0;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
            s=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        t=t*10+c-'0';
        c=getchar();
    }
    return s*t;
}
void add(int from,int to)
{
    len[++cnt].to=to;
    len[cnt].next=head[from];
    head[from]=cnt;
}
int tarjan(int x,int fa)
{
    int child=0,lowx;
    lowx=dfn[x]=++deep;
    for(R int k=head[x];k;k=len[k].next)
    {
        int to=len[k].to;
        if(!dfn[to])
        {
            ++child;
            int lowto=tarjan(to,x);
            lowx=min(lowx,lowto);
            if(lowto>=dfn[x])
                cut[x]=1;
        }
        else if(to!=fa&&dfn[x]>dfn[to])
            lowx=min(lowx,dfn[to]);//low[to]可能走到另一个环,到达更浅的祖先.
            //此时lowto<dfn[x]而忽略x这个可能的割点.
    }
    if(fa<0&&child==1)
        cut[x]=0;
    low[x]=lowx;
    return lowx;
}
int main()
{
    //freopen("s.in","r",stdin);
    //freopen("s.out","w",stdout);
    n=fd(),m=fd();
    for(R int i=1;i<=m;++i)
    {
        int from=fd(),to=fd();
        add(from,to),add(to,from);
    }
    for(R int i=1;i<=n;++i)
        if(!dfn[i])
            tarjan(i,-1);
    for(R int i=1;i<=n;++i)
        if(cut[i])
        {
            ++ans;
            q[++lenth]=i;
        }
    printf("%d
",ans);
    sort(q+1,q+1+lenth);
    for(R int i=1;i<=lenth;++i)
        printf("%d ",q[i]);
    return 0;
}

附上dalao博客

原文地址:https://www.cnblogs.com/xqysckt/p/11194867.html