poj 3694 雙連通 縮點 橋 LCA

/*
    雙連通  縮點  橋  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    400005

struct 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;
}


發佈了52 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章