Tarjan整理

概括一下

  1. 强连通分量

    1
    2
    3
    4
    5
    6
    7
    if(!V[v]){
    tarjan(v);
    low[u]=min(low[u],low[v]);
    }
    else if(vis[v]){
    low[u]=min(low[u],dfn[v]);
    }
  2. 点双连通分量

    1
    2
    3
    if(low[v]>=dfn[u]){
    记录一个答案;
    }
  3. 边双连通分量

    1
    2
    3
    4
    5
    6
    if(low[v]>dfn[u]){
    把 u<->v 这条边删掉;
    }
    int main(){
    洪水填充找连通块;
    }
  4. 割点(割顶)

    1
    还是寒假讲的,真的忘了当初怎么想的了……

Code

  1. 强连通分量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    #include<bits/stdc++.h>
    using namespace std;
    vector<int>G[50001];
    int n,m,tot,at;
    bool vis[50001],V[50001];
    int dfn[50001];
    int low[50001];
    vector<int>ans[50001];
    stack<int>s;
    void tarjan(int u){
    s.push(u);
    V[u]=true;
    vis[u]=true;
    dfn[u]=++tot;
    low[u]=dfn[u];
    for(auto v:G[u]){
    if(!V[v]){
    tarjan(v);
    low[u]=min(low[u],low[v]);
    }
    else if(vis[v]){
    low[u]=min(low[u],dfn[v]);
    }
    }
    if(low[u]==dfn[u]){
    at++;
    int tmp;
    do{
    tmp=s.top();
    ans[at].push_back(tmp);
    vis[tmp]=false;
    s.pop();
    }while(!s.empty()&&tmp!=u);
    }
    }
    int main(){
    #ifdef USE_FILE_IO
    freopen("code.in","r",stdin);
    freopen("code.out","w",stdout);
    #endif
    cin>>n>>m;
    for(int i=1;i<=m;i++){
    int op,u,v;
    cin>>u>>v;
    G[u].push_back(v);
    }
    for(int i=1;i<=n;i++){
    tot=0;
    if(!V[i])tarjan(i);
    }
    cout<<at<<endl;
    for(int i=1;i<=at;i++){
    sort(ans[i].begin(),ans[i].end());
    // if(ans[i].size()>mx){
    // mx=ans[i].size();
    // mxw=i;
    // }
    }
    sort(ans+1,ans+at+1);
    for(int i=1;i<=at;i++){
    for(auto j:ans[i]){
    cout<<j<<" ";
    }
    cout<<endl;
    }
    return 0;
    }
  2. 点双连通分量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    #include<bits/stdc++.h>
    // #define int long long
    using namespace std;
    const int N=500005;
    int n,m,idx;
    vector<int>G[N];
    int dfn[N],low[N];
    stack<int>s;
    int ansCnt;
    vector<int>ans[N];
    void tarjan(int fa,int u){
    s.push(u);
    dfn[u]=low[u]=++idx;
    for(auto v:G[u]){
    if(v==fa)continue;
    if(!dfn[v]){
    tarjan(u,v);
    low[u]=min(low[u],low[v]);
    if(low[v]>=dfn[u]){
    ansCnt++;
    // while(s.top()!=u){
    // ans[ansCnt].push_back(s.top());
    // s.pop();
    // }
    int tmp;
    do{
    tmp=s.top();
    ans[ansCnt].push_back(tmp);
    s.pop();
    }while(tmp!=v);
    ans[ansCnt].push_back(u);
    }
    }
    else{
    low[u]=min(low[u],dfn[v]);
    }
    }
    if(fa==-1&&G[u].size()==0){
    ansCnt++;
    ans[ansCnt].push_back(u);
    }
    }
    signed main(){
    #ifdef USE_FILE_IO
    freopen("code.in","r",stdin);
    freopen("code.out","w",stdout);
    #endif
    // cin>>n>>m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
    int u,v;
    // cin>>u>>v;
    scanf("%d%d",&u,&v);
    if(u!=v){
    G[u].push_back(v);
    G[v].push_back(u);
    }
    }
    for(int i=1;i<=n;i++){
    if(!dfn[i]){
    // idx=0;
    while(!s.empty())s.pop();
    tarjan(-1,i);
    }
    }
    printf("%d\n",ansCnt);
    for(int i=1;i<=ansCnt;i++){
    printf("%d ",ans[i].size());
    for(auto j:ans[i]){
    printf("%d ",j);
    }
    printf("\n");
    }
    return 0;
    }
  3. 边双连通分量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int N=500005;
    int n,m,idx;
    vector<int>G[N];
    int dfn[N],low[N];
    stack<int>s;
    int ansCnt;
    vector<int>ans[N];
    unordered_map<int,int>bridge;
    unordered_map<int,int>edge;
    unordered_map<int,int>ok;
    void tarjan(int fa,int u){
    s.push(u);
    dfn[u]=low[u]=++idx;
    for(auto v:G[u]){
    if(v==fa)continue;
    if(!dfn[v]){
    tarjan(u,v);
    low[u]=min(low[u],low[v]);
    if(low[v]>dfn[u]){
    bridge[u*N+v]=!ok[u*N+v];
    bridge[v*N+u]=!ok[u*N+v];
    }
    }
    else{
    low[u]=min(low[u],dfn[v]);
    }
    }
    }
    bool vis[N];
    void dfs(int u){
    vis[u]=1;
    ans[ansCnt].push_back(u);
    for(auto v:G[u]){
    if(!vis[v]&&(!bridge[u*N+v])){
    dfs(v);
    }
    }
    }
    signed main(){
    #ifdef USE_FILE_IO
    freopen("code.in","r",stdin);
    freopen("code.out","w",stdout);
    #endif
    // cin>>n>>m;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++){
    int u,v;
    // cin>>u>>v;
    scanf("%lld%lld",&u,&v);
    if(u!=v&&!edge[u*N+v]){
    G[u].push_back(v);
    G[v].push_back(u);
    edge[u*N+v]=1;
    edge[v*N+u]=1;
    }
    else if(u!=v){
    ok[u*N+v]=1;
    ok[v*N+u]=1;
    }
    }
    for(int i=1;i<=n;i++){
    if(!dfn[i]){
    tarjan(-1,i);
    }
    }
    for(int i=1;i<=n;i++){
    if(!vis[i]){
    ansCnt++;
    dfs(i);
    }
    }
    printf("%lld\n",ansCnt);
    for(int i=1;i<=ansCnt;i++){
    printf("%ld ",ans[i].size());
    for(auto j:ans[i]){
    printf("%lld ",j);
    }
    printf("\n");
    }
    return 0;
    }
  4. 割点(割顶)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=20000;
    vector<int>G[maxn+100];
    int dfn[maxn+100],low[maxn+100],tot;
    int n,m;
    vector<int>cut;
    void Tarjan(int u,int root){
    low[u]=dfn[u]=++tot;
    int cnt=0;
    for(auto v:G[u]){
    if(!dfn[v]){
    Tarjan(v,root);
    low[u]=min(low[u],low[v]);
    if(low[v]>=dfn[u])cnt++;
    }
    else{
    low[u]=min(low[u],dfn[v]);
    }
    }
    if(u==root){
    if(cnt>1)cut.push_back(u);
    }
    else
    if(cnt>0)cut.push_back(u);
    }
    int main(){
    #ifdef USE_FILE_IO
    freopen("code.in","r",stdin);
    freopen("code.out","w",stdout);
    #endif
    cin>>n>>m;
    for(int i=1;i<=m;i++){
    int u,v;
    cin>>u>>v;
    G[u].push_back(v);
    G[v].push_back(u);
    }
    for(int i=1;i<=n;i++){
    if(dfn[i])continue;
    Tarjan(i,i);
    }
    sort(cut.begin(),cut.end());
    cout<<cut.size()<<endl;
    for(int i=0;i<cut.size();i++)
    cout<<cut[i]<<" ";
    return 0;
    }

Tarjan整理
https://shicj.pages.dev/2025/01/01/Tarjan整理/
作者
shicj
发布于
2025年1月1日
许可协议