poj 3694 雙連通 縮點 橋 LCA

來源:互聯網
上載者:User

/*
    雙連通  縮點  橋  LCA  
    
    題意:給一個無向連通圖,問每次新加進一條邊後,圖中橋的數目。  重邊算一條。

    思路:求雙連通分量,利用並查集縮點,形成一棵樹,樹邊肯定都是橋,然後每對點x,y,找原圖中x,y點對應的新圖中的點,如果不是一個點,則向上>找它們的LCA,因為它們之間連了一條邊,所以這些點到它們的LCA之間的邊都不是割邊了,找LCA時,先將兩點上升到同一層次,然後一起再向上找父親節點
,其間遇到橋就把橋的標記刪除,並且答案減1

    

    思路還是挺簡單的,不過我做了很久,把調試過程記錄下來吧。準確來說的對拍調試過程。
    1. ans是當前橋的數目。兩種方法,一種是在Tarjan裡面if (dfn[u] <= low[v]) ans++;第二種是直接ans = Order - 1; Order表示連通塊的個數,>因為縮點之後是一棵樹,那樹的沒一條邊都是橋,所以橋數 = 點數 - 1.

    2. 題目沒有說當有重邊的時候要怎麼辦,實驗表明,當有重邊的時候只算一條。那Tarjan的時候有兩個辦法防止無盡迴圈,一是在讀邊的時候就注意>有沒有重邊,則在Tarjan裡面可以用visit[]來記錄某邊是否已經用過;而是改邊判為點判,即記錄點的父節點,然後遞迴之前判斷一下。
       感覺還是第二種比較省事。這裡記一下代碼:
          for(int k = head[u]; k != -1; k = edge[k].next) {
                int v  = edge[k].v;
                if(fa[u] == v) continue;    //!!
                if(!dfn[v]) {
                    fa[v] = u;...
            ....
    3. 建縮點之後的樹還是挺簡單的,可以當作模板了。
    4. 樸素lca的各個語句的位置弄混了...
    5. 下面這句調試語句棒=幫了很多忙
        for(int i = 1; i <= n; i++) printf("# %d\t%d\t%d\n", i, belong[i], deep[belong[i]]);      //查了這裡才知道
       

*/



#include <stdio.h>#include <algorithm>#include <string.h>using namespace std;#define     debug   printf("!\n")#define     MAXN    200005#define     MAXM    400005struct Edge { int v, next; } edge[MAXM * 2];int  head[MAXN], low[MAXN], dfn[MAXN], belong[MAXN], father[MAXN], deep[MAXN];bool in_stack[MAXN], ok[MAXN], visit[MAXM * 2], used[MAXN];int edge_num, Index, Order, S_top, ans , n;int S[MAXN * 2];int fa[MAXN];void add_edge(int u, int v){    edge[edge_num].v = v;    edge[edge_num].next = head[u];    head[u] = edge_num++;}void init(){    edge_num = 0;    Index = 1;    Order = 0;    S_top = 0;    ans = 0;    memset(head, -1, sizeof(head));    memset(dfn,  0 , sizeof(dfn));    memset(low,  0 , sizeof(low));    memset(visit, false, sizeof(visit));    memset(deep, -1, sizeof(deep));    memset(in_stack, false, sizeof(in_stack));    memset(used, false, sizeof(used));    memset(fa, 0, sizeof(fa));      //有用嗎?????}void Tarjan(int u){    S[S_top++] = u;    in_stack[u] = true;    dfn[u] = low[u] = Index++;    for(int k = head[u]; k != -1; k = edge[k].next) { //if(!visit[k]) {        //visit[k] = visit[k^1] = true;        int v = edge[k].v;        if(fa[u] == v) continue;        //還有這裡。。。         if(!dfn[v]) {            fa[v] = u;            Tarjan(v);            low[u] = min(low[u], low[v]);            // if(dfn[u] <= low[v]) ans++;  用這個ans的話在只有一個連通塊的時候會錯???        } else if(in_stack[v]) low[u] = min(low[u], dfn[v]);    }    if(low[u] == dfn[u]) {        int x;        do {                x = S[--S_top];            belong[x] = Order;            in_stack[x] = false;        } while(x != u);        Order++;    }}void build(int root, int f){    for(int k = head[root]; k != -1; k = edge[k].next) {        int v = edge[k].v;        if(v == f || used[v]) continue;        used[v] = true;        if(belong[v] == belong[root]) build(v, root);        else {            father[belong[v]] = belong[root];            build(v, root);        }    }}int get_depth(int node){    if(-1 != deep[node]) return deep[node];    return deep[node] = 1 + get_depth(father[node]);}void Query(int x, int y){    if(deep[x] < deep[y]) swap(x, y);    while(deep[x] > deep[y]) {        ans -= (!ok[x]);        ok[x] = true;        x = father[x];          //之前把這一句放在while開始,當然錯啦!!    }    if(x == y) return ;    while(true) {        ans -= (!ok[x]) + (!ok[y]);        ok[x] = ok[y] = true;        x = father[x], y = father[y];       //位置很重要,不確定的時候類比一下即可。        if(x == y) return ;    }}int main(){    int Case = 0, query, x, y, m;    while(scanf("%d%d", &n, &m) != EOF && n) {        init();        while(m--) {            scanf("%d%d", &x, &y);            add_edge(x, y);     //先預設沒有重邊了..結果題目是有重邊的            add_edge(y, x);        }        Tarjan(1);        ans = Order - 1;        build(1, -1);        deep[belong[1]] = 0;        //belong[1] != 0!!        for(int i = 0; i < Order; i++) if(-1 == deep[i]) get_depth(i);        //for(int i = 1; i <= n; i++) printf("# %d\t%d\t%d\n", i, belong[i], deep[belong[i]]);      //查了這裡才知道        for(int i = 0; i < Order; i++) ok[i] = false;        printf("Case %d:\n", ++Case);        scanf("%d", &query);        while(query--) {            scanf("%d%d", &x, &y);            Query(belong[x], belong[y]);            printf("%d\n", ans);        }        printf("\n");    }    return 0;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.