求助有赏85ptsRE——关于以缩点栈形式Tarjan求边双

P8436 【模板】边双连通分量

Echoternity @ 2022-12-06 22:15:22

在与同机房巨佬谈边双的时候,觉得写第二遍 dfs 有点麻烦了,就想能不能用栈的形式(也就是缩点那种形式)求出边双,然后胡了一下,发现可做,然后RE 85pts,下数据发现该方法似乎不能处理自环和重边。

求助万能的谷友,这个方法能不能改进,悬赏两个关注。

给代码:

// ----- Eternally question-----
// Problem: P8436 【模板】边双连通分量
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P8436
// Memory Limit: 256 MB
// Time Limit: 3000 ms
// Written by: Eternity
// Time: 2022-12-06 21:43:48
// ----- Endless solution-------

#include<bits/stdc++.h>
#define re register
typedef long long ll;
template<class T>
inline void read(T &x)
{
    x=0;
    char ch=getchar(),t=0;
    while(ch<'0'||ch>'9') t|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    if(t) x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1){ read(x),read(x1...); }
template<class T>
inline void write(T x)
{
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
template<>
inline void write(bool x){ putchar(x?'1':'0'); }
template<>
inline void write(char c){ putchar(c); }
template<>
inline void write(char *s){ while(*s!='\0') putchar(*s++); }
template<>
inline void write(const char *s){ while(*s!='\0') putchar(*s++); }
template<class T,class ...T1>
inline void write(T x,T1 ...x1){ write(x),write(x1...); }
template<class T>
inline bool checkMax(T &x,T y){ return x<y?x=y,1:0; }
template<class T>
inline bool checkMin(T &x,T y){ return x>y?x=y,1:0; }
const int MAXN=5e5+10,MAXM=2e6+10;
int N,M;
struct G
{
    int next,to;
}Edge[MAXM<<1];
int Head[MAXN],Total;
inline void addEdge(int u,int v)
{
    Edge[++Total]=(G){Head[u],v};Head[u]=Total;
    Edge[++Total]=(G){Head[v],u};Head[v]=Total;
}
int Dfn[MAXN],Low[MAXN],Stk[MAXN],Cnt,Top;
bool Ins[MAXN];
int Dcc[MAXN],Sizc[MAXN],Dc;
void Tarjan(int x,int last)
{
    Dfn[x]=Low[x]=++Cnt,Ins[x]=1,Stk[++Top]=x;
    for(int e=Head[x],v;e;e=Edge[e].next)
    {
        if((v=Edge[e].to)==last) continue;
        if(!Dfn[v])
        {
            Tarjan(v,x);
            checkMin(Low[x],Low[v]);
        }
        else if(Ins[v]) checkMin(Low[x],Dfn[v]);
        if(Low[v]>Dfn[x])
        {
            // write("bridge:",x,' ',v,"\nnow stack:");
            // for(int i=1;i<=Top;++i) write(Stk[i],' ');
            // puts("");
            ++Dc;
            while(Stk[Top]!=v)
            {
                Dcc[Stk[Top]]=Dc,++Sizc[Dc];
                Ins[Stk[Top]]=0,--Top;
            }
            Dcc[v]=Dc,++Sizc[Dc],Ins[v]=0,--Top;
        }
    }
}
std::vector<int>Scc[MAXN];
int main()
{
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    read(N,M);
    for(int i=1,u,v;i<=M;++i){ read(u,v);addEdge(u,v); }
    for(int x=1;x<=N;++x) if(!Dfn[x])
    {
        Tarjan(x,0);
        if(Top)
        {
            ++Dc;
            while(Top)
            {
                Dcc[Stk[Top]]=Dc,++Sizc[Dc];
                Ins[Stk[Top]]=0,--Top;
            }
        }
    }
    for(int i=1;i<=N;++i) Scc[Dcc[i]].push_back(i);
    write(Dc,'\n');
    for(int i=1;i<=Dc;++i,puts(""))
    {
        write(Scc[i].size(),' ');
        for(int x:Scc[i]) write(x,' ');
    }
    return 0;
}
/*

*/

by yhk1001 @ 2022-12-07 08:17:46

@Eternal

我的代码

int dfn[N + 5], low[N + 5], tim;
int stk[N + 5], top;
int cnt;
vector<int> edcc[N + 5];
void dfs(int u, int fa, int in)
{
    dfn[u] = low[u] = ++tim;
    stk[++top] = u;
    for (int i = head[u]; i; i = nxt[i])
    {
        int v = to[i];
        if (v == fa && (i ^ 1) == in)
            continue;
        if (!dfn[v])
        {
            dfs(v, u, i);
            low[u] = min(low[u], low[v]);
        }
        else
            low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u])
    {
        cnt++;
        while (stk[top] != u)
        {
            edcc[cnt].push_back(stk[top]);
            top--;
        }
        edcc[cnt].push_back(stk[top]);
        top--;
    }
    return ;
}

by yhk1001 @ 2022-12-07 08:20:02

我的写法类似有向图的tarjan


by happybob @ 2022-12-07 08:26:19

@Eternal 可以啊,我过了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;

const int N = 4e6 + 5;

int e[N], ne[N], h[N], idx;

int dcc_cnt, id[N], low[N], dfn[N], idx2, n, m;
stack<int> s;
bool is_bri[N];
int d[N], sz[N];
vector<int> v[N];

void add(int u, int v)
{
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

void tarjan(int u, int from)
{
    low[u] = dfn[u] = ++idx2;
    s.push(u);
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if (!dfn[j])
        {
            tarjan(j, i);
            low[u] = min(low[u], low[j]);
        }
        else if (i != (from ^ 1))
        {
            low[u] = min(low[u], dfn[j]);
        }
        if (dfn[u] < low[j])
        {
            is_bri[i] = is_bri[i ^ 1] = 1;
        }
    }
    if (dfn[u] == low[u])
    {
        dcc_cnt++;
        int y;
        do
        {
            y = s.top();
            s.pop();
            id[y] = dcc_cnt;
            v[dcc_cnt].push_back(y);
            sz[dcc_cnt]++;
        } while (y != u);
    }
}

int main()
{
    memset(h, -1, sizeof h);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v), add(v, u);
    }
    for (int i = 1; i <= n; i++)
    {
        if (!dfn[i]) tarjan(i, -1);
    }
    printf("%d\n", dcc_cnt);
    for (int i = 1; i <= dcc_cnt; i++)
    {
        printf("%d ", sz[i]);
        for (int j = 0; j < sz[i]; j++) printf("%d ", v[i][j]);
        printf("\n");
    }
    return 0;
}

by wei_xin @ 2022-12-07 08:42:09

错误点在于你没有理解 tarjan。

思考一下,为什么求 S-DCC 的时候要记录 Instack?


by wei_xin @ 2022-12-07 08:43:32

事实上,你的思路是对的,但用这种方式求解 E-DCC 时,不考虑 Instack 与否的影响。

如果思考后还是不明白可以私信我,或者看我的博客中 目录-数学-图论-图的连通性 部分。


by Echoternity @ 2022-12-07 09:24:50

我是小丑了,因为当时做的时候翻遍全网都没有写栈形式的,就以为这个方法没人用,但看起来是我知识浅薄了。


by Echoternity @ 2022-12-07 09:25:23

此贴结。


|