UVA610 Street Directions

版权声明:蒟蒻写文章不容易啊,各位大佬转载要告诉我一声,QAQ https://blog.csdn.net/qq_38944163/article/details/85556512

我扔: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....

猜你喜欢

转载自blog.csdn.net/qq_38944163/article/details/85556512