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
此贴结。