强连通分量、边双联通分量、割点割边及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;
}
}
可以自由转载

浙公网安备 33010602011771号