连通性小结(贴一些模板而已)
感觉学习了第二遍确实比第一遍理解得好很多!!!(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("\n"); FOR(i,0,edgecut_cnt){ printf("%d %d\n",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\n",ans); printf("%d",pos[1]); FOR(i,2,n+1){ printf(" %d",pos[i]); } printf("\n"); } 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\n",cnt); printf("%d",ans[pos[1]]); FOR(i,2,m+1){ printf(" %d",ans[pos[i]]); } printf("\n"); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。