点双联通分量和边双联通分量如何选择?

先讲一下 ,双联通分量 一定是用于 无向图
考虑什么时候需要用边双联通分量呢?,考虑给你的是一个一般图,需要你把联通的点都缩起来,视作一个点的情况,就是说割点可以反复访问,就是说割点和其他点都是等效的,并没有区别,,(正常情况都是边双联通分量,如果出到点双连通分量,一般难度很高)

点双联通分量使用时,通常,有些点有些特殊,比如,你通过一个环,必须得从一个点入,另一个点出,像下面图,无法通过这个环从1抵达6,这种的情况就需要用点双联通分量缩点图1
再点双之中,这种可以通过环来从1抵达6
在这里插入图片描述
但是在边双中两图完全没有区别
以后可以试试这两种走法是否有区别来判断使用点双还是边双

边双

/*
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define sqrt(a) sqrtl(a)
#define abs(a) llabs(a)
#define pow(a) powl(a)
typedef  pair<int,int> pi ;
#define if1(x) for(int i =1 ;i<=x;i++)
#define if0(x) for(int i = 0;i<x;i++)
#define jf0(x) for(int j = 0;j<x;j++)
#define jf1(x) for(int j = 1;j<=x;j++)
#define pb push_back
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const int N = 2e5+10;



vector<pair<int,int>> edg[N];//终点和边的编号
vector<int> nedg[N];//新边图
int id[N];
int dfn[N],low[N],tim;
stack<int> stk;
vector<vector<int> > dcc;//边双连通分量
bool vis[N];
int cnt,idx;
int n,m;//点数目,边数目
int nidx ;//新点的编号
void tarjan(int u,int in_edg)
{
    dfn[u] = low[u] = ++tim;//更新时间戳
    stk.push(u);vis[u] = 1;
    for(auto &j:edg[u]){
        if(j.second ==(in_edg^1))continue;//是回边,则不走
        if(!dfn[j.first]){//没有访问
            tarjan(j.first,j.second);
            low[u] = min(low[u],low[j.first]);
        }else if(vis[j.first])
            low[u] = min(low[u],low[j.first]);
    }
    if(dfn[u] == low[u]){//桥边到了
        vector<int>te;
        while(1){
            int y = stk.top();stk.pop();
            te.push_back(y);
            vis[y] = 0;
            if(u == y)break;
        }dcc.push_back(te);     
    }
}
void suodian(){
    for(auto j:dcc){
        nidx++;
        for(auto k:j)id[k] = nidx;//标记点的联通 块属于谁
    }
    if1(n){
        for(auto [a,b]:edg[i]){
            if(id[i]!=id[a]){
                nedg[id[i]].push_back(id[a]);
            }
        }
    }
}
void solve(){
    cin>>n>>m;
    idx = 0;
    if0(m){
        int a,b;
        cin>>a>>b;
        edg[a].push_back({b,idx++});
        edg[b].push_back({a,idx++});
    }
    if1(n){
        if(!dfn[i])tarjan(i,-1);
    }
    suodian();
    if1(n)cout<<i<<" "<<id[i]<<"||";
    cout<<endl;
    //输出新图
    if1(nidx){
        cout<<i<<":";
        for(auto j:nedg[i])cout<<j<<" ";
        cout<<endl;
    }

}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr); 
    int t=1;
    // cin>>t;
    while (t--)
    {
        solve();
    }
    return 0;
}
/*
9 11
1 2
2 3
3 1
1 4
1 5
5 6
4 7
1 7
2 7
5 8
9 6
输出
初始边和新边
1 5||2 5||3 5||4 5||5 4||6 2||7 5||8 3||9 1||
新图
1:2
2:4 1
3:4
4:5 2 3
5:4

*/

点双

/*
*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define sqrt(a) sqrtl(a)
#define abs(a) llabs(a)
#define pow(a) powl(a)
typedef  pair<int,int> pi ;
#define if1(x) for(int i =1 ;i<=x;i++)
#define if0(x) for(int i = 0;i<x;i++)
#define jf0(x) for(int j = 0;j<x;j++)
#define jf1(x) for(int j = 1;j<=x;j++)
#define pb push_back
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;
const int N = 2e5+10;




vector<int> edg[N];//终点和边的编号
set<int>  nedg[N];
int dfn[N],low[N],tim;
stack<int> stk;
vector<int > dcc[N];//点双连通分量
bool cut[N];//割点
int id[N];
int cnt,idx,root,nidx;
int n,m;//点数目,边数目
void tarjan(int u)
{
    dfn[u] = low[u] = ++tim;//更新时间戳
    stk.push(u);
    if(!edg[u].size()){//孤立点
        dcc[++cnt].push_back(u);return;
    }
    int child = 0;
    for(auto  j:edg[u]){
        if(!dfn[j]){
            tarjan(j);
            low[u] = min(low[u],low[j]);
            if(low[j] >= dfn[u]){
                child++;
                if(u!=root||child>1){
                    cut[u] = 1;//是割点
                }
                cnt++;
                while(1){
                    int z = stk.top();stk.pop();
                    dcc[cnt].push_back(z);
                    if(z == j)break;
                }
                dcc[cnt].push_back(u);
            }
        }
        else{
            low[u] = min(low[u],dfn[j]);
        }
    }

}
void suodian(){
    nidx = cnt;
    if1(n*m){
        if(cut[i]){
            id[i] = ++nidx;//给割点重新编号

        }
    }
    if1(cnt){
        // cout<<i<<":";
        for(auto j:dcc[i]){
            if(cut[j] == 1){
                nedg[i].insert(id[j]);
                nedg[id[j]].insert(i);
            }
            //割点已经编好新下标了
            else id[j] = i;//以连通块序号来对块中顶点编号
            // cout<<j<<" "<<id[j]<<",";

        }
        // cout<<endl;
    }
}
void solve(){
    cin>>n>>m;
    if0(m){
        int a,b;
        cin>>a>>b;
        edg[a].push_back(b);
        edg[b].push_back(a);
    }
    if1(n){
        if(!dfn[i])root = i,tarjan(i);
    }
    suodian();
    if1(n)cout<<i<<" "<<id[i]<<"||";
    cout<<endl;
    //输出新图
    if1(nidx){
        cout<<i<<":";
        for(auto j:nedg[i])cout<<j<<" ";
        cout<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr); 
    int t=1;
    // cin>>t;
    while (t--)
    {
        solve();
    }
    return 0;
}
/*
8 10
1 2
3 4 
2 3
2 4
1 4
1 5
5 6
5 7
5 8
7 8

*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夭辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值