边的双连通分量,求桥! 点的双连通分量,求割点!

点的双连通分量
#include <bits/stdc++.h>
using namespace std;

const int N = 4e6+9;
int n,m;
int numE,head[N];
int root,dfn[N],low[N];
int timestamp,stk[N];
int top,dcc_cnt;
bool cut[N];
vector<int> dcc[N];
struct E {
    int next,to;
}e[N];

void add(int a,int b) {
    e[numE] = {head[a],b};
    head[a] = numE++;
}

inline void tarjan(int u) {
    dfn[u] = low[u] = ++timestamp;
    stk[++top] = u;
    if(u == root && head[u] == -1) {
        dcc_cnt++;
        dcc[dcc_cnt].push_back(u);
        return;
    }
    int flag = 0;
    for(int i=head[u]; ~i; i=e[i].next) {
        int v = e[i].to;
        if(!dfn[v]) {
            tarjan(v);
            low[u] = min(low[u],low[v]);
            if(dfn[u] <= low[v]) {
                flag++;
                if(u != root || flag > 1) cut[u] = true;
                ++dcc_cnt;
                int y;
                do {
                    y = stk[top--];
                    dcc[dcc_cnt].push_back(y);
                } while(y != v);
                dcc[dcc_cnt].push_back(u);
            }
        } else low[u] = min(low[u],dfn[v]);
    }
    if(flag == 0 && u == root) dcc[++dcc_cnt].push_back(u); //自环根据需求可以删掉!
}

int main() {
    cin >> n >> m;
    memset(head,-1,sizeof head);
    for(int i=1; i<=m; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1; i<=n; i++) {
        if(!dfn[i]) {
            root = i;
            tarjan(i);
        }
    }
    cout<<dcc_cnt<<"\n";
    for(int i=1; i<=dcc_cnt; i++) {
        printf("%d ",dcc[i].size());
        for(auto v : dcc[i]) {
            printf("%d ",v);
        }
        putchar('\n');
    }
    return 0;
}
//边的双连通分量
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;
const int N = 4e6+9;

vector<int> dcc[N];
int head[N],numE;
int n,m;
struct E {
    int next,to;
}e[N];

int dfn[N],low[N],stk[N],id[N];
int timestamp,dcc_cnt,top;
bool is_bridge[N];

void add(int a,int b) {
    e[numE] = {head[a],b};
    head[a] = numE++;
}

void tarjan(int u,int from) {
    dfn[u] = low[u] = ++timestamp;
    stk[++top] = u;
    for(int i=head[u]; ~i; i=e[i].next) {
        int v = e[i].to;
        if(!dfn[v]) {
            tarjan(v,i);
            low[u] = min(low[u],low[v]);
            if(dfn[u] < low[v])
                is_bridge[i] = is_bridge[i ^ 1] = true;
        } else if(i != (from ^ 1)) {
            low[u] = min(low[u],low[v]);
        }
    }
    if(dfn[u] == low[u]) {
        ++dcc_cnt;
        int y;
        do {
            y = stk[top--];
            id[y] = dcc_cnt;
            dcc[dcc_cnt].push_back(y);
        } while(y != u);
    }
}

int main() {
    memset(head,-1,sizeof head);
    cin >> n >> m;
    for(int i=1; i<=m; i++) {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1; i<=n; i++) {
        if(!dfn[i]) {
            tarjan(i,0);
        }
    }
    printf("%d\n",dcc_cnt);
    for(int i=1; i<=dcc_cnt; i++) {
        printf("%d ",dcc[i].size());
        for(auto v : dcc[i]) {
            printf("%d ",v);
        }
        puts("");
    }
    return 0;
}


//无向图e-Dcc的缩点!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5+9;
int head[N],numE;
int dfn[N],low[N],bridge[N];
int num,Dcc,c[N];
struct Edge
{
    int next,to;
}e[N],ex[N];
void add(Edge e[],int a,int b)
{
    e[numE].next = head[a];
    e[numE].to = b;
    head[a] = numE++;
}
void tarjan(int x,int in_edge)
{
    dfn[x] = low[x] = ++num;
    for(int i=head[x]; ~i; i=e[i].next)
    {
        int y = e[i].to;
        if(!dfn[y])
        {
            tarjan(y,i);
            low[x] = min(low[x],low[y]);
            if(low[y] > dfn[x])
                bridge[i] = bridge[i ^ 1] = 1;
        }
        else if(i != (in_edge ^ 1))
            low[x] = min(low[x],dfn[y]);
    }
}
void dfs(int u)
{
    c[u] = Dcc;
    for(int i=head[u]; ~i; i=e[i].next)
    {
        int v = e[i].to;
        if(c[v] || bridge[i]) continue;
        dfs(v);
    }
}
void solve()
{
    memset(head,-1,sizeof head);
    int n,m;
    cin >> n >> m;
    for(int i=1; i<=m; i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(e,a,b);
        add(e,b,a);
    }
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i]) tarjan(i,-1);
    }
    for(int i=1; i<=n; i++)
    {
        if(!c[i])
        {
            ++Dcc;
            dfs(i);
        }
    }
    printf("this map have %d dcc\n",Dcc);
    for(int i=1; i<=n; i++)
    {
        printf("i = %d c[i] = %d\n",i,c[i]);
    }
    printf("---------------------------------------\n");
    numE = 0;
    for(int i=0; i<2*m; i++)
    {
        int x = e[i ^ 1].to,y = e[i].to;
        if(c[x] == c[y]) continue;
        printf("x = %d y = %d\n",x,y);
        add(ex,x,y);
    }
    printf("缩点后的图为:\n");
    for(int i=0; i<numE; i+=2)
    {
        printf("%d %d\n",ex[i].to,ex[i ^ 1].to);
    }
}
int main()
{
    solve();
    return 0;
}
//9 11
//1 2
//1 5
//1 6
//2 3
//2 5
//3 4
//5 4
//6 9
//6 8
//8 9
//6 7

//求边的双连通分量!
//1 用搜索删除桥的方式!
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5+9;
int head[N],numE;
int dfn[N],low[N],bridge[N];
int num,Dcc,c[N];
struct Edge 
{
    int next,to;
}e[N];
void add(int a,int b)
{
    e[numE].next = head[a];
    e[numE].to = b;
    head[a] = numE++;
}
void tarjan(int x,int in_edge)
{
    dfn[x] = low[x] = ++num;
    for(int i=head[x]; ~i; i=e[i].next) 
    {
        int y = e[i].to;
        if(!dfn[y]) 
        {
            tarjan(y,i);
            low[x] = min(low[x],low[y]);
            if(low[y] > dfn[x]) 
                bridge[i] = bridge[i ^ 1] = 1;
        }
        else if(i != (in_edge ^ 1)) 
            low[x] = min(low[x],dfn[y]);
    }
}
void dfs(int u) 
{
    c[u] = Dcc;
    for(int i=head[u]; ~i; i=e[i].next)
    {
        int v = e[i].to;
        if(c[v] || bridge[i]) continue;
        dfs(v);
    }
}
void solve()
{
    memset(head,-1,sizeof head);
    int n,m;
    cin >> n >> m;
    for(int i=1; i<=m; i++) 
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1; i<=n; i++) 
    {
        if(!dfn[i]) tarjan(i,-1);
    }
    for(int i=1; i<=n; i++)
    {
        if(!c[i]) 
        {
            ++Dcc;
            dfs(i);
        }
    }
    printf("this map have %d dcc\n",Dcc);
    for(int i=1; i<=n; i++) 
    {
        printf("i = %d c[i] = %d\n",i,c[i]);
    }
}
int main()   
{
    solve();
    return 0;
}

P3388 【模板】割点(割顶)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e6+9;
int head[N],numE;
int dfn[N],low[N],bridge[N];
int num,root,ans;
bool cut[N];
struct Edge
{
    int next,to;
}e[N];
void add(int a,int b)
{
    e[numE] = {head[a],b};
    head[a] = numE++;
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++num;
    int flag = 0;
    for(int i=head[x]; ~i; i=e[i].next)
    {
        int y = e[i].to;
        if(!dfn[y])
        {
            tarjan(y);
            low[x] = min(low[x],low[y]);
            if(low[y] >= dfn[x])
            {
                flag++;
                if(x != root || flag > 1) cut[x] = true;
            }
        }
        else low[x] = min(low[x],dfn[y]);
    }
}
void solve()
{
    memset(head,-1,sizeof head);
    int n,m;
    cin >> n >> m;
    for(int i=1; i<=m; i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(a == b) continue;
        // 这里不continue,是否可以? 应该是可以的!
        add(a,b);
        add(b,a);
    }
    for(int i=1; i<=n; i++)
    {
        if(!dfn[i]) root = i,tarjan(i);
    }
    for(int i=1; i<=n; i++) if(cut[i]) ans++; //必须最后统计才可以!
    cout<<ans<<"\n";
    for(int i=1; i<=n; i++)
    {
        if(cut[i]) printf("%d ",i);
    }
}
int main()
{
    solve();
    return 0;
}

求割点:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5+9;
int head[N],numE;
int dfn[N],low[N],bridge[N];
int num,root;
bool cut[N];
struct Edge 
{
    int next,to;
}e[N];
void add(int a,int b)
{
    e[numE].next = head[a];
    e[numE].to = b;
    head[a] = numE++;
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++num;
    int flag = 0;
    for(int i=head[x]; ~i; i=e[i].next) 
    {
        int y = e[i].to;
        if(!dfn[y]) 
        {
            tarjan(y);
            low[x] = min(low[x],low[y]);
            if(low[y] >= dfn[x]) 
            {
                flag++;
                if(x != root || flag > 1) cut[x] = true;
            }
        }
        else low[x] = min(low[x],dfn[y]);
    }
}
void solve()
{
    memset(head,-1,sizeof head);
    int n,m;
    cin >> n >> m;
    for(int i=1; i<=m; i++) 
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(a == b) continue;
        // 这里不continue,是否可以?
        add(a,b);
        add(b,a);
    }
    for(int i=1; i<=n; i++) 
    {
        if(!dfn[i]) root = i,tarjan(i);
    }
    for(int i=1; i<=n; i++) 
    {
        if(cut[i]) printf("%d ",i);
    }
    puts("\nare cut_vertexes");
}
int main()   
{
    solve();
    return 0;
}
求桥:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 2e5+9;
int head[N],numE;
int dfn[N],low[N],bridge[N];
int num;
struct Edge 
{
    int next,to;
}e[N];
void add(int a,int b)
{
    e[numE].next = head[a];
    e[numE].to = b;
    head[a] = numE++;
}
void tarjan(int x,int in_edge)
{
    dfn[x] = low[x] = ++num;
    for(int i=head[x]; ~i; i=e[i].next) 
    {
        int y = e[i].to;
        if(!dfn[y]) 
        {
            tarjan(y,i);
            low[x] = min(low[x],low[y]);
            if(low[y] > dfn[x]) 
                bridge[i] = bridge[i ^ 1] = 1;
        }
        else if(i != (in_edge ^ 1)) 
            low[x] = min(low[x],dfn[y]);
    }
}
void solve()
{
    memset(head,-1,sizeof head);
    int n,m;
    cin >> n >> m;
    for(int i=1; i<=m; i++) 
    {
        int a,b;
        scanf("%d%d",&a,&b);
        add(a,b);
        add(b,a);
    }
    for(int i=1; i<=n; i++) 
    {
        if(!dfn[i]) tarjan(i,-1);
    }
  //只需要枚举一条边,另一条边为i ^ 1;
    for(int i=0; i<numE; i+=2) 
    {
        if(bridge[i]) printf("%d %d\n",e[i ^ 1].to,e[i].to);
    }
    //for(int i=0; i<numE; i++)
    //{
    //    if(bridge[i]) printf("%d %d\n",e[i ^ 1].to,e[i].to);
    //}
}
int main()   
{
    int t;
    //cin >> t;
    t = 1;
    while(t--) solve();
    return 0;
}