UVA610 Street Directions
我扔:https://www.luogu.org/problemnew/show/UVA610
题意
已知一堆双向边,要求把尽可能多的双向边改成单向边,但任意两点仍然可以互相到达。
题解
不难想到,割边如果定单向的话那就不能互相到达了,所以先把所有的割边(即桥)求出来,然后割边为定为双向边,其余定为单向边,至于边的方向就是直接按照搜索树上的方向就行了。
code:
#include<bits/stdc++.h>
#define N 1000005
using namespace std;
struct edge{
int v, nxt;
}e[N];
int p[N], eid;
void init(){
memset(p, -1, sizeof p);
eid = 0;
}
void insert(int u, int v){
e[eid].v = v;
e[eid].nxt = p[u];
p[u] = eid ++;
}
int dfn[N], low[N], in_sta[N], sta[N], sz, tot, top, belong[N], size[N];
void tarjan(int x, int fa){//这里缩的是边双联通分量
dfn[x] = low[x] = ++ tot;
sta[++ top] = x;
in_sta[x] = 1;
for(int i = p[x]; i + 1; i = e[i].nxt){
int v = e[i].v;
if(i == (fa^1)) continue;//即不能走父边
if(!dfn[v]) tarjan(v, i), low[x] = min(low[x], low[v]);
else if(in_sta[v]) low[x] = min(low[x], dfn[v]);
}
if(dfn[x] == low[x]){//缩点
sz ++;
while(sta[top] != x){
size[sz] ++;
belong[sta[top]] = sz;
in_sta[sta[top]] = 0;
top --;
}
size[sz] ++;
belong[sta[top]] = sz;
in_sta[sta[top]] = 0;
top --;
}
}
int n, m, ans, d[N], vis[N], t;
vector<int> a[N];
void dfs(int x, int fa){
dfn[x] = 1;
for(int i = p[x]; i + 1; i = e[i].nxt){
int v = e[i].v;
if(i == (fa^1)) continue; //同样不能走父边
if(!dfn[v]) dfs(v, i); //如果没访问就访问
if(!d[i]){ //如果不为割边
if(!vis[i]) printf("%d %d\n", x, v);//并且没被定向就按照搜索树上的方向即x--->v输出
vis[i] = vis[i^1] = 1;//表示已经定向
}else{//如果为割边就定为双向边
printf("%d %d\n", x, v);
printf("%d %d\n", v, x);
}
}
}
int main(){
while(~scanf("%d%d", &n, &m)){
if(!n && !m) break;
t ++; printf("%d\n\n", t);
memset(belong, 0, sizeof belong);
memset(size, 0, sizeof size);
memset(d, 0, sizeof d);
top = 0;
tot = 0;
sz = 0;
memset(dfn, 0, sizeof dfn);
memset(low, 0, sizeof low);
memset(in_sta, 0, sizeof in_sta);
memset(vis, 0, sizeof vis);//繁杂的初始化
init();
for(int i = 1; i <= m; i ++){
int u, v;
scanf("%d%d", &u, &v);
insert(u, v);
insert(v, u);
}
tarjan(1, -1); //图保证联通,所以只需要从1开始就行了
for(int i = 1; i <= n; i ++){
for(int j = p[i]; j + 1; j = e[j].nxt){
int v = e[j].v;
if(belong[i] == belong[v]) continue;//如果在同一个边双联通分量就跳过
d[j] = 1;//如果在同一个边双联通分量,即为割边,做个标记
d[j^1] = 1;//反向边同理
}
}
memset(dfn, 0, sizeof dfn);
dfs(1, -1); //按照搜索树上的顺序输出
printf("#\n");
}
return 0;
}
interesting....