CF118E Bertown roads
DFS 树。
首先我们要知道,在求割点或桥的时候有一种不用 Tarjan 的算法:DFS 树。如果不了解,我推荐神仙的博客 link(其实是翻译)。
我们考虑证明,如果存在桥一定不满足,否则一定可以构造。
- 如果存在桥:假设我们把这条边定向为 \(u\to v\),那么显然 \(v\) 走不到 \(u\) 了。
- 如果不存在桥:我们考虑把 DFS 树上所有边定向为父亲到儿子,其他非树边定向为返祖边。首先显然根能到达所有节点。由于没有桥,在每个点 \(x\) 经过几个树边和一个返祖边,一定能到达一个 \(x\) 的长辈。这样一直走一定能到达根。
于是这题做完了。当然 Tarjan 也可以做,具体可以看其他神仙的做法。
// By: Little09
// Problem: CF118E Bertown roads
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF118E
// Memory Limit: 250 MB
// Time Limit: 5000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
const int N=500005;
int n,m;
vector<int>t[N],g[N],id[N];
bool vis[N],cut[N],used[N];
int rt[N],cnt,w[N],dis[N],s[N];
int X[N],Y[N];
inline int read()
{
int F=1,ANS=0;
char C=getchar();
while (C<'0'||C>'9')
{
if (C=='-') F=-1;
C=getchar();
}
while (C>='0'&&C<='9')
{
ANS=ANS*10+C-'0';
C=getchar();
}
return F*ANS;
}
void dfs1(int x,int fa)
{
vis[x]=1;
dis[x]=dis[fa]+1;
for (int i=0;i<t[x].size();i++)
{
if (t[x][i]==fa) continue;
if (vis[t[x][i]]==0)
{
dfs1(t[x][i],x);
used[id[x][i]]=1;
g[x].push_back(t[x][i]);
g[t[x][i]].push_back(x);
}
else if (dis[x]>dis[t[x][i]])
{
s[x]=min(s[x],dis[t[x][i]]);
}
}
}
void dfs2(int x,int fa)
{
for (int i=0;i<g[x].size();i++)
{
if (g[x][i]!=fa)
{
dfs2(g[x][i],x);
s[x]=min(s[x],s[g[x][i]]);
}
}
if (fa!=0&&s[x]>=dis[x]) cut[x]=1;
}
int main()
{
n=read(),m=read();
for (int i=1;i<=m;i++)
{
int x=read(),y=read();
t[x].push_back(y),t[y].push_back(x);
id[x].push_back(i),id[y].push_back(i);
X[i]=x,Y[i]=y;
}
for (int i=1;i<=n;i++) s[i]=N;
for (int i=1;i<=n;i++)
{
if (vis[i]==0)
{
dfs1(i,0),dfs2(i,0);
}
}
for (int i=1;i<=n;i++)
{
if (cut[i]==1)
{
puts("0");
return 0;
}
}
for (int i=1;i<=m;i++)
{
int x=X[i],y=Y[i];
if (dis[x]>dis[y]) swap(x,y);
if (used[i]==0) swap(x,y);
printf("%d %d\n",x,y);
}
return 0;
}