连通性小结(贴一些模板而已)
感觉学习了第二遍确实比第一遍理解得好很多!!!(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;
}版权声明:本文为博主原创文章,未经博主允许不得转载。

浙公网安备 33010602011771号