连通性小结(贴一些模板而已)

感觉学习了第二遍确实比第一遍理解得好很多!!!(Mark一下,hihocoder 52,53,54,55)

连通性的四个部分:

1:割点和桥

2:边的双连通分量

3:点的双连通分量

4:有向图的强连通分量

最重要的显然是第一个,因为后面的基本上都是根据第一个来的吧。

概念:

割点:去掉这个点以后使得连通图不再连通

桥:去掉这条边使得连通图不再连通

边的双连通分量:双连通分量里面,任意去掉一条边,图仍是连通的

点的双连通分量:双连通分量里面,任意去掉一个点,图仍是连通的

有向图的强连通分量:强连通分量中的任意两个点都有至少两条路径可以到达

基本上所有类型只需要通过一个Tarjan的算法就能够全部实现。

割点: 1:如果该点为根,那么child > 1那么这个点就是割点

2:如果该点不为根,那么low[v] >= low[u] 这个点就为割点

桥: low[v] > low[u] 那么(u,v)的边就是一个桥,特别要注意和割点不同的是根节点的情况,这里不需要特判


下面的连通分量需要记录功能,所以用stack维护

边的双连通分量: 如果把该图的所有桥去掉,剩下来的所有连通图都是一个边的双连通分量。只需要满足low[v] > dfn[u] ,那么从栈顶出栈,一直到v的所有点都在一个边的双 连通 分量里面。因为当前节点不太好用v来表示。所有当low[u] == dfn[u]的时候那么出栈到u的所有点都是边的双连通分量。

点的双连通分量: 割点两边的子图分别是一个点的双连通分量(与边的双连通分量比较地看)。这里出栈的话,就是到当前的节点(割点),需要特别注意的有两个:1,根节 点的判定。2:只找了割点,会发现并不是所有节点都在相应的点的双连通分量里面了,按照割点分完以后,这个时候stack里面剩下的所有边都在一个点的 双连通分量里面。

有向图的强连通分量:主要是一个缩点的操作。我们把一个强连通分量缩成一个点。然后再重新建图,topo排序一下就可以搞定了。

割点和桥:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 20020
#define M 100010

using namespace std;

int pre[N],low[N],head[N],dfs_clock,m,n,edge_cnt;
int cut[N];
bool iscut[N];
bool isedgecut[M<<1];

struct Edge{
    int u,v;
    int nt;
    bool operator < (Edge rhs)  const{
        if(u == rhs.u)  return v < rhs.v;
        return u < rhs.u;
    }
}edge[M<<1];

Edge edgecut[M<<1];

void AddEdge(int u,int v){
    edge[edge_cnt].u = u;
    edge[edge_cnt].v = v;
    edge[edge_cnt].nt = head[u];
    head[u] = edge_cnt++;
}

void dfs(int u,int fa){
    low[u] = pre[u] = ++dfs_clock;
    int child = 0;
    for(int i = head[u];i != -1;i = edge[i].nt){
        int v = edge[i].v;
        if(v == fa) continue;
        if(!pre[v]){
            child++;
            dfs(v,u);
            low[u] = min(low[v],low[u]);
            if(low[v] >= pre[u])    iscut[u] = true;
            if(low[v] > pre[u]) isedgecut[i] = true;
        }
        else{
            low[u] = min(low[u],pre[v]);
        }
    }
    if(fa == -1 && child <= 1){
        iscut[u] = false;
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        int u,v;
        memset(head,-1,sizeof(head));
        memset(pre,0,sizeof(pre));
        memset(iscut,false,sizeof(iscut));
        memset(isedgecut,false,sizeof(isedgecut));
        FOR(i,0,m){
            scanf("%d%d",&u,&v);
            AddEdge(u,v);
            AddEdge(v,u);
        }
        dfs(1,-1);
        int cut_cnt = 0,edgecut_cnt = 0;
        FOR(i,1,n+1){
            if(iscut[i])    cut[cut_cnt++] = i;
        }
        FOR(i,0,edge_cnt){
            if(isedgecut[i]){
                edgecut[edgecut_cnt].u = min(edge[i].u,edge[i].v);
                edgecut[edgecut_cnt++].v = max(edge[i].u,edge[i].v);
            }
        }
        sort(edgecut,edgecut+edgecut_cnt);
        if(cut_cnt){
            printf("%d",cut[0]);
            FOR(i,1,cut_cnt){
                printf(" %d",cut[i]);
            }
        }
        else printf("Null");
        printf("
");
        FOR(i,0,edgecut_cnt){
            printf("%d %d
",edgecut[i].u,edgecut[i].v);
        }
    }
    return 0;
}

边的双连通分量:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstdlib>
#include <stack>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 20020
#define M 100010

using namespace std;

typedef  pair<int,int> pii;
vector <int> G[N];
stack <int> s;
int pos[N],dfn[N],dfs_clock,low[N],n,m,ans,num[N];

void dfs(int u,int fa){
    dfn[u] = low[u] = ++dfs_clock;
    s.push(u);
    FOR(i,0,G[u].size()){
        int v = G[u][i];
        if(!dfn[v]){
            dfs(v,u);
            low[u] = min(low[u],low[v]);
        }
        else if(v != fa){
            low[u] = min(dfn[v],low[u]);
        }
    }
    if(low[u] == dfn[u]){
        int cnt = 0,minx = INF;
        ans++;
        while(!s.empty()){
            num[cnt++] = s.top();
            minx = min(minx,num[cnt-1]);
            s.pop();
            if(num[cnt-1] == u) break;
        }
        FOR(i,0,cnt){
            pos[num[i]] = minx;
        }
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        FOR(i,0,N)  G[i].clear();
        int u,v;
        FOR(i,0,m){
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        memset(dfn,0,sizeof(dfn));
        dfs_clock = 0;
        ans = 0;
        while(!s.empty())   s.pop();
        dfs(1,-1);
        printf("%d
",ans);
        printf("%d",pos[1]);
        FOR(i,2,n+1){
            printf(" %d",pos[i]);
        }
        printf("
");
    }
    return 0;
}

有向图的强连通分量:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 20020
#define M 100010

using namespace std;

vector <int> G[N];
vector <int> NG[N];
stack  <int> s;

int low[N],dfn[N],dfs_clock,cnt,pos[N],l[M],r[M];
ll w[N],nw[N],res[N];
int n,m,ans;
bool mark[N];
bool vis[N];

void init(){
    FOR(i,0,N)  G[i].clear();
    FOR(i,0,N)  NG[i].clear();
    dfs_clock = 0;
    cnt = 0;
    memset(dfn,0,sizeof(dfn));
    memset(mark,false,sizeof(mark));
    memset(nw,0,sizeof(nw));
    memset(vis,false,sizeof(vis));
    while(!s.empty())   s.pop();
}

void dfs(int u){
    dfn[u] = low[u] = ++dfs_clock;
    s.push(u);
    mark[u] = true;
    FOR(i,0,G[u].size()){
        int v = G[u][i];
        if(!dfn[v]){
            dfs(v);
            low[u] = min(low[u],low[v]);
        }
        else if(mark[v]) low[u] = min(low[u],dfn[v]);
    }
    if(low[u] == dfn[u]){
        cnt++;
        while(!s.empty()){
            int tem = s.top(); s.pop();
            pos[tem] = cnt;
            nw[cnt] += w[tem];
            mark[tem] = false;
            if(tem == u) break;
        }
    }
}

void Build_Graph(){
    FOR(i,0,m){
        if(pos[l[i]] != pos[r[i]])  NG[pos[l[i]]].push_back(pos[r[i]]);
    }
}

ll solve(){
    queue <int> q;
    q.push(pos[1]);
    memset(res,0,sizeof(res));
    ll ans = nw[pos[1]];
    res[pos[1]] = nw[pos[1]];
    while(!q.empty()){
        int u = q.front();q.pop();
        ans = max(ans,res[u]);
        FOR(i,0,NG[u].size()){
            int v = NG[u][i];
            res[v] = max(res[v],res[u] + nw[v]);
            q.push(v);
        }
    }
    return ans;
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        init();
        FOR(i,1,n+1)    cin >> w[i];
        //int u,v;
        FOR(i,0,m){
            scanf("%d%d",&l[i],&r[i]);
            G[l[i]].push_back(r[i]);
        }
        dfs(1);
        Build_Graph();
        cout<<solve()<<endl;
    }
    return 0;
}

点的双连通分量:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#define INF (1<<30)
#define ll long long
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define N 20020
#define M 100010

using namespace std;

int n,m;

int head[N],edge_cnt;
struct Edge{
    int u,v;
    int nt,id;
}edge[M<<1];

void AddEdge(int u,int v,int id){
    edge[edge_cnt].u = u;
    edge[edge_cnt].v = v;
    edge[edge_cnt].nt = head[u];
    edge[edge_cnt].id = id;
    head[u] = edge_cnt++;
}

stack <int> s;
int dfn[N],low[N],dfs_clock,pos[M],cnt,ans[M];

void dfs(int u,int fa){
    dfn[u] = low[u] = ++dfs_clock;
    int child = 0;
    for(int i = head[u];i != -1;i = edge[i].nt){
        int v = edge[i].v;
        if(!dfn[v]){
            s.push(edge[i].id);
            child++;
            dfs(v,u);
            low[u] = min(low[u],low[v]);
            if(fa == -1 && child > 1){
                cnt ++;
                int minx = INF;
                while(!s.empty()){
                    int tem = s.top(); s.pop();
                    pos[tem] = cnt;
                    minx = min(minx,tem);
                    if(tem == edge[i].id) break;
                }
                ans[cnt] = minx;
            }
            if(fa != -1 && low[v] >= dfn[u]){
                cnt ++;
                int  minx = INF;
                while(!s.empty()){
                    int tem = s.top(); s.pop();
                    pos[tem] = cnt;
                    minx = min(minx,tem);
                    if(tem == edge[i].id) break;
                }
                ans[cnt] = minx;
            }
        }
        else if(v != fa && dfn[v] < dfn[u]){
            s.push(edge[i].id);
            low[u] = min(low[u],dfn[v]);
        }
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        int u,v;
        memset(head,-1,sizeof(head));
        edge_cnt = 0;
        FOR(i,1,m+1){
            scanf("%d%d",&u,&v);
            AddEdge(u,v,i);
            AddEdge(v,u,i);
        }
        while(!s.empty())   s.pop();
        dfs_clock = 0;
        memset(dfn,0,sizeof(dfn));
        cnt = 0;
        dfs(1,-1);
        if(!s.empty())  cnt++;
        int  minx = INF;
        while(!s.empty()){
            int tem = s.top(); s.pop();
            pos[tem] = cnt;
            minx = min(minx,tem);
        }
        ans[cnt] = minx;
        printf("%d
",cnt);
        printf("%d",ans[pos[1]]);
        FOR(i,2,m+1){
            printf(" %d",ans[pos[i]]);
        }
        printf("
");
    }
    return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/hqwhqwhq/p/4811897.html