割点算法

一、定义:

在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。
如果某个割点集合只含有一个顶点X(也即{X}是一个割点集合),那么X称为一个割点。
 
二、模板
  1. 邻接矩阵实现(时间复杂度太高,没什么用)
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int n,m,root,w[sizen][sizen];
    int num[sizen]={0},low[sizen]={0},flag[sizen]={0},t=0;
    void dfs(int cur, int father);
    
    int main()
    {
        cin>>n>>m;
        
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                w[i][j]=0;
        for(int i=1;i<=m;i++)
        {
            cin>>a>>b;
            w[a][b]=1;
            w[b][a]=1;
        }
        root=1;
        dfs(1,root);
        
    }
    
    void dfs(int cur,int father)
    {
        int child=0;
        t++;
        num[cur]=t;
        low[cur]=t;
        
        for(int i=1;i<=n;i++)
        {
            if(w[cur][i]==1)
            {
                if(num[i]==0)
                {
                    child++;
                    dfs(i,cur);
                    low[cur]=min(low[cur],low[i]);
                    if(cur!=root && low[i]>=num[cur])
                        flag[cur]=1;
                    if(cur==root && child==2)
                        flag[cur]==1;
                }
                else if(i != father)
                {
                    low[cur]=min(low[cur],num[i]);
                }
            }
        }
    }
  2. 邻接表实现
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #define sizen 5000
    #define sizem 500050
    using namespace std;
    int n,m,root,next[2*sizem],first[sizen],u[2*sizem],v[2*sizem];
    int num[sizen]={0},low[sizen]={0},flag[sizen]={0},t=0;
    void dfs(int cur, int father);
    
    int main()
    {
        cin>>n>>m;
        memset(next,-1,sizeof(next));
        memset(first,-1,sizeof(first));
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u[i],&v[i]);
            u[m+i]=v[i];
            v[m+i]=u[i];
            next[i]=first[u[i]];
            first[u[i]]=i;
            next[m+i]=first[u[m+i]];
            first[u[m+i]]=m+i;
        }
        m*=2;
        
        root=1;
        dfs(1,root);
        
        for(int i=1;i<=n;i++)
        if(flag[i])cout<<i<<endl;
    }
    
    void dfs(int cur,int father)
    {
        int child=0;
        t++;
        num[cur]=t;
        low[cur]=t;
        int i=first[cur];
        
        while(i!=-1)
        {
            if(num[v[i]]==0)
            {
                child++;
                dfs(v[i],cur);
                low[cur]=min(low[cur],low[v[i]]);
                if(cur!=root && low[v[i]]>=num[cur])
                    flag[cur]=1;
                if(cur==root && child==2)
                    flag[cur]=1;
            }
            else if(v[i]!=father)
            {
                low[cur]=min(low[cur],num[v[i]]);
            }
            i=next[i];
        }
    }
  3. tarjan图不一定联通的情况:

    Luogu P3388 【模板】割点(割顶)

    for(int i=1;i<=n;i++)
    {
        if(num[i]==0)
        root=i,dfs(root,root);
    }

    每个点搜一遍

  4. 割边:修改 >= 为 >(原因自己想)
    if(cur!=root && low[v[i]]>num[cur])
        flag[cur]=1;
  5. +一点点强连通分量

    Luogu P3469 [POI2008]BLO-Blockade

     每个公民都想拜访其他所有公民一次(在主人所在的城镇)。所以,一共会有n*(n-1)次拜访。对于每个被决定的城镇,如果它被封锁,有多少访问不会发生,输出结果。
    挂一个WA代码,待AC:(使用的并查集解决每个版块个数的问题,但是好像用DFS更简单)
    #include<cstdio>
    #include<ctime>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define sizen 100050
    #define sizem 500050
    using namespace std;
    int n,m,root,next[2*sizem],first[sizen],u[2*sizem],v[2*sizem];
    int num[sizen]={0},low[sizen]={0},flag[sizen]={0},t=0;
    int forefather[sizen],ccount[sizen];
    void merge(int x,int y);
    void dfs(int cur, int father);
    long long int ltk(int k);
    int main()
    {
        //freopen("testdata.in","r",stdin);
        //freopen("point.out","w",stdout);
        cin>>n>>m;
        memset(next,-1,sizeof(next));
        memset(first,-1,sizeof(first));
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u[i],&v[i]);
            u[m+i]=v[i];
            v[m+i]=u[i];
            next[i]=first[u[i]];
            first[u[i]]=i;
            next[m+i]=first[u[m+i]];
            first[u[m+i]]=m+i;
        }
        m*=2;
        
        root=1;dfs(root,root);
        
        
        int count=0;
        for(int i=1;i<=n;i++)
        if(!flag[i])cout<<2*(n-1)<<endl;
        else 
        {
            cout<<ltk(i)+2*(n-1)<<endl;
        }
        //if(flag[i])count++;
        //cout<<count<<endl;
        //for(int i=1;i<=n;i++)
        //if(flag[i])cout<<flag[i]<<' ';
    }
    
    void dfs(int cur,int father)
    {
        int child=0;
        t++;
        num[cur]=t;
        low[cur]=t;
        int i=first[cur];
        
        while(i!=-1)
        {
            if(num[v[i]]==0)
            {
                child++;
                dfs(v[i],cur);
                low[cur]=min(low[cur],low[v[i]]);
                if(cur!=root && low[v[i]]>=num[cur])
                    flag[cur]=1;
                if(cur==root && child==2)
                    flag[cur]=1;
            }
            else if(v[i]!=father)
            {
                low[cur]=min(low[cur],num[v[i]]);
            }
            i=next[i];
        }
    }
    long long int ltk(int k)
    {
        for(int i=1;i<=n;i++)forefather[i]=i;
        for(int i=1;i<=n;i++)
        {
            if(i==k)continue;
            int r=first[i];
            if(v[r]==k)r=next[r];
            while(r!=-1)
            {
                merge(v[r],i);
                r=next[r];
                if(v[r]==k)r=next[r];
            }    
        }
        forefather[k]=0;
        sort(forefather+1,forefather+n+1);
        int top=1;
        ccount[top]=1;
        for(int i=3;i<=n;i++)
        {
            if(forefather[i-1]!=forefather[i])
            ccount[++top]=1;
            else ccount[top]++;
        }
    
        
        long long int ans=0;
        for(int i=1;i<=top;i++)
        for(int j=i+1;j<=top;j++)
        ans+=ccount[i]*ccount[j];
        ans*=2;
        return ans;
    }
    
    void merge(int x,int y)
    {
        int tx=x,ty=y;
        while(forefather[x]!=x)
        x=forefather[x];
        while(forefather[y]!=y)
        y=forefather[y];
        forefather[x]=y;
        while(forefather[tx]!=tx)
        {
            int j=forefather[tx];
            forefather[tx]=y;
            tx=j;
        }
        while(forefather[ty]!=ty)
        {
            int j=forefather[ty];
            forefather[ty]=y;
            ty=j;
        }
    }
  6. 待解决题单:POJ1144 POJ1523 HDU4738  POJ3694
原文地址:https://www.cnblogs.com/Cindy-Chan/p/11069280.html