强连通分量、边双联通分量、割点割边及targan

强连通分量定义:如果u存在到达v的路径v存在到达u的路径,我们称u、v是强连通的(在有向图上)

为了求出图上的强连通分量,我们定义:

  • dfn[i]为点i的DFS序

  • low[i]为从点i出发,不经过父亲最高能跑到的节点的DFS序

然后枚举每个点,如果没有被访问,即开栈,做一遍DFS

DFS途中,遇到以下情况

  • v未被访问,即DFS(v),并用\(low_v\)更新\(low_u\)
  • v被访问过,且在栈中,则\(low_v=dfn_v\)
  • 否则不操作

如果操作完成后,还是dfn[u]=low[u],则为一个新的强连通分量,弹栈

//stack为栈
void dfs(ll x){//跑DFS
	dfn[x]=low[x]=++op;//新的DFS序
	ok[x]=1;
	st.push(x);
	for(int i=head[x];i;i=es[i].nxt){
		ll v=es[i].v;
		if(!dfn[v]){
			dfs(v);
		}
		if(ok[v]){//更新
			low[x]=min(low[x],low[v]);
		}
	}
	//vector<ll>c;
	if(dfn[x]==low[x]){//根
		ll cnt=0;//记录有几个元素
		nu++;//第几个强连通分量
		while(1){
			ll v=st.top();
			//c.push_back(v);
			ok[v]=0;//已经有了
			gen[v]=nu;//元素属于第几个强连通分量
			st.pop();//弹栈
			if(v==x){
				break;
			}
		} 
	}
}

割边:在无向图上,去掉某条边后图由联通变为不联通,则该边为割边

割点:在无向图上,去掉某个点后图由联通变为不联通,则该点为割点

  • dfn[i]为点i的DFS序

  • low[i]为从点i出发,不经过父亲最高能跑到的节点的DFS序

如果找到一个\(u->v\),满足\(dfn[u]<=low[v]\),则找到了割点

如果找到一个\(u->v\),满足\(dfn[u]<low[v]\),则找到了割边

割点:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m;
ll dfn[1000000],low[1000000],rep,ok[1000000];
ll head[1000000],tot;
ll root;
struct nood{
	ll v;
	ll nxt;
}es[1000000];
void adde(ll x,ll y){
	es[++tot].v=y,es[tot].nxt=head[x],head[x]=tot;
}
void doo(ll x,ll root){
	dfn[x]=low[x]=++rep;
	ll reo=0;
	for(int i=head[x];i;i=es[i].nxt){
		ll v=es[i].v;
		if(!dfn[v]){
			doo(v,root);
			low[x]=min(low[x],low[v]);
			if(low[v]>=dfn[x]){
				reo++;
			}
		}
		else{
			low[x]=min(low[x],dfn[v]);
		}
	}
	if(root==x&&reo>=2){
		ok[x]=1;
	}
	if(root!=x&&reo>=1){
		ok[x]=1;
	}
}
int main(){
	cin>>n>>m;
	ll x,y;
	for(int i=1;i<=m;i++){
		cin>>x>>y;
		adde(x,y);
		adde(y,x);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			doo(i,i);
		}
	}
	ll ans=0;
	for(int i=1;i<=n;i++){
		if(ok[i]){
			ans++;
		}
	}
	cout<<ans<<endl;
	for(int i=1;i<=n;i++){
		if(ok[i]){
			cout<<i<<" ";
		}
	}
} 

割边:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m;
ll head[1000000],tot;
struct nood{
	ll v;
	ll nxt;
}es[1000000];
void adde(ll x,ll y){
	es[++tot].v=y,es[tot].nxt=head[x],head[x]=tot;
}
ll dfn[1000000],low[1000000],pd[1000000];
ll id;//dfn 
ll ans[1000000];
void tar(ll x,ll edge){
	dfn[x]=low[x]=++id;
	for(int i=head[x];i;i=es[i].nxt){
		ll v=es[i].v;
		if(!dfn[v]){
			tar(v,i);
			low[x]=min(low[x],low[v]);//更新 
			if(low[v]>dfn[x]){//判定条件 
				pd[i]=pd[(i^1)]=1;
			}
		}
		else if(i!=(edge^1)){//走一条边可以到x点 
			low[x]=min(low[x],dfn[v]);
		}
	}
}
int main(){
	tot=1;
	cin>>n>>m;
	ll u,v;
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		if(u!=v){
			adde(u,v);
			adde(v,u);
		}
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tar(i,0);
		}
	}
	for(int i=2;i<=tot;i+=2){
		if(pd[i]){
			cout<<es[i^1].v<<" "<<es[i].v;
			cout<<endl;
		}
	}
}

若一张无向连通图不存在桥(割边),则称它为“边双连通图”

寻找很简单,不要走割边跑DFS即可

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m;
ll head[4000005],tot;
struct nood{
	ll v;
	ll nxt;
}es[4000005];
void adde(ll x,ll y){
	es[++tot].v=y,es[tot].nxt=head[x],head[x]=tot;
}
ll dfn[4000005],low[4000005],pd[4000005];
ll id,idp,sum;//dfn,联通块 
ll ans[4000005],vis[4000005];
vector< vector<ll> > ve;
void tar(ll x,ll edge){
	dfn[x]=low[x]=++id;
	for(int i=head[x];i;i=es[i].nxt){
		ll v=es[i].v;
		if(!dfn[v]){
			tar(v,i);
			low[x]=min(low[x],low[v]);
			if(low[v]>dfn[x]){
				//cout<<i<<"*"<<endl;
				pd[i]=pd[(i^1)]=1;
			}
		}
		else if(i!=(edge^1)){
			low[x]=min(low[x],dfn[v]);
		}
	}
}
void dfss(ll x){
	vis[x]=idp;
	sum++;
	ve[idp-1].push_back(x);
	for(int i=head[x];i;i=es[i].nxt){
		ll v=es[i].v;
		if(!vis[v]&&!pd[i]){
			dfss(v);
		}
	}
}
int main(){
	tot=1;
	cin>>n>>m;
	ll u,v;
	for(int i=1;i<=m;i++){
		cin>>u>>v;
		if(u!=v){
			adde(u,v);
			adde(v,u);
		}
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tar(i,0);
		}
	}
	for(int i=2;i<=tot;i+=2){
		if(pd[i]){
			//cout<<es[i^1].v<<" "<<es[i].v;
			//printf("%d %d\n",es[i^1].v,es[i].v);
		}
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			ve.push_back(vector <ll>());
			idp++;
			sum=0;
			dfss(i);
		}
	}
	cout<<idp<<endl;
	for(int i=0;i<idp;i++){
		cout<<ve[i].size()<<" ";
		for(int j=0;j<ve[i].size();j++){
			cout<<ve[i][j]<<" ";
		} 
		cout<<endl;
	}
}
posted @ 2024-10-11 20:40  MistyPost  阅读(70)  评论(0)    收藏  举报