题目链接:https://codeforces.com/problemset/problem/732/F
12.1 题意
给定一个 n ( 2 ≤ n ≤ 4 ⋅ 1 0 5 ) n(2 \le n \le 4 \cdot 10^5) n(2≤n≤4⋅105) 个节点和 m ( 1 ≤ m ≤ 4 ⋅ 1 0 5 ) m(1 \le m \le 4 \cdot 10^5) m(1≤m≤4⋅105) 条边的无向连通图,保证没有自环和重边。
现在要给每条边定一个方向,定义 r i r_i ri 为 i i i 点在定向之后可以到达的点的数量。
问如何对每一条边进行定向,使得 min i { r i } \min_i \left\{r_i \right\} mini{ri} 最大。
12.2 解题过程
我们先用 Tarjan 求出无向图中所有的桥,之后对于每一个边双连通分量进行 DFS,这样可以将每一个边双联通分量转化为强连通分量。
这样转化的目的是使每一个连通分量中的点相互可达,即一个强连通分量中点的 r i r_i ri 值至少为这个连通分量的点的个数。
现在我们只需确定所有的桥的方向。
做法很贪心,我们找到规模最大的强连通分量,其他的桥都指向它就可以保证答案最优。
实现时,只需从规模最大的强连通分量出发,进行 DFS,将经过的桥的反方向标记为答案即可。
时间复杂度: O ( n ) O(n) O(n)。
12.3 代码
int n, m;
int head[maxn], cnt, pos;
int dfn[maxn], low[maxn];
int choice[2 * maxn], col[maxn], num[maxn], point[maxn];
bool bridge[2 * maxn], vis[maxn];
struct edge
{
int u, v, nxt;
int type;
} Edge[2 * maxn];
void init()
{
memset(head, -1, sizeof(head));
memset(choice, -1, sizeof(choice));
cnt = 0, pos = 0;
}
void addedge(int u, int v, int type)
{
Edge[cnt].u = u;
Edge[cnt].type = type;
Edge[cnt].v = v;
Edge[cnt].nxt = head[u];
head[u] = cnt++;
}
void tarjan(int id, int edg)
{
dfn[id] = low[id] = ++pos;
int flag = 0;
for(int i = head[id]; i != -1; i = Edge[i].nxt)
{
int v = Edge[i].v;
if(!dfn[v])
{
tarjan(v, i);
low[id] = min(low[id], low[v]);
if(dfn[id] < low[v]) bridge[i] = bridge[i ^ 1] = true;
}
else if(i != (edg ^ 1)) low[id] = min(low[id], dfn[v]);
}
}
void dfs(int id, int c)
{
col[id] = c;
num[c]++;
if(!point[c]) point[c] = id;
for(int i = head[id]; i != -1; i = Edge[i].nxt)
{
int v = Edge[i].v;
if(bridge[i]) continue;
if(choice[i] == -1)
{
choice[i] = choice[i ^ 1] = Edge[i].type;
}
if(col[v] == c) continue;
dfs(v, c);
}
}
void dfs2(int id)
{
vis[id] = true;
for(int i = head[id]; i != -1; i = Edge[i].nxt)
{
int v = Edge[i].v;
if(vis[v]) continue;
if(bridge[i] && choice[i] == -1)
{
choice[i] = choice[i ^ 1] = Edge[i ^ 1].type;
}
dfs2(v);
}
}
int main()
{
int u, v;
scanf("%d%d", &n, &m);
init();
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v, 0);
addedge(v, u, 1);
}
for(int i = 1; i <= n; i++)
{
if(!dfn[i]) tarjan(i, -1);
}
int tot = 0;
for(int i = 1; i <= n; i++)
{
if(!col[i])
{
dfs(i, ++tot);
}
}
int id = -1, maxvalue = 0;
for(int i = 1; i <= tot; i++)
{
if(num[i] > maxvalue)
{
maxvalue = num[i];
id = i;
}
}
dfs2(point[id]);
printf("%d\n", maxvalue);
for(int i = 0; i < cnt; i += 2)
{
u = Edge[i].u;
v = Edge[i].v;
if(choice[i] == 0) printf("%d %d\n", u, v);
else printf("%d %d\n", v, u);
}
return 0;
}