0w1

UVA 1108 Mining Your Own Business ( 點雙連通分量 )

題目的圖論模型是要求將儘量少的點標記,使得刪除任意一點後每個聯通分量有至少一個被標記的點。
我們要發現一般來說標記在割點上不會比較好,而且在同一個點雙連通分量上標記兩個以上的點是無意義的。再說如果一個點雙連通分量含兩個以上的割點,我們可以不需要標記該點雙連通分量的任何點,因為無論哪個割點消失,它們都可以藉由另一個割點到別的點雙連通分量,然而最後也一定會有一個奇度數的點雙連通分量,所以我們得到結論,只有奇度數( = 洽一割點 )的點雙連通分量需要標記其中任一個點。最後要記得特判例外,只有一個點雙連通分量的情況,就只能隨便挑兩個點標記。
另外要特別注意的是這題需要做離散化,因為題目給的點id 是散亂的,看了LRJ 提供的代碼覺得他的寫法很方便就學了一下。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MAXN = 5e4 + 4;

int kase;
int n;
vector<int> G[MAXN];

struct ID{
    map<int, int> m;
    int cnt;
    ID(): cnt(0){}
    int get(int x){
        if( m.count( x ) ) return m[ x ];
        return m[ x ] = cnt++;
    }
};

void init(){
    for(int i = 0; i <= n; ++i)
        G[i].clear();
}

int dfs_clock, pre[MAXN], low[MAXN];
int bcc_cnt, bccno[MAXN], is_cut[MAXN];
vector<int> bcc[MAXN];
stack<pii> stk;

int dfs(int u, int fa){
    int lowu = pre[u] = ++dfs_clock;
    int child = 0;
    for(int v: G[u]){
        if( v == fa ) continue;
        if( !pre[ v ] ){
            ++child;
            stk.push( pii( u, v ) );
            int lowv = dfs( v, u );
            if( lowv >= pre[u] ){
                is_cut[ u ] = 1;
                bcc[ ++bcc_cnt ].clear();
                for(;;){
                    int nu = stk.top().first, nv = stk.top().second;
                    stk.pop();
                    if( bccno[ nu ] != bcc_cnt ){
                        bccno[ nu ] = bcc_cnt;
                        bcc[ bcc_cnt ].push_back( nu );
                    }
                    if( bccno[ nv ] != bcc_cnt ){
                        bccno[ nv ] = bcc_cnt;
                        bcc[ bcc_cnt ].push_back( nv );
                    }
                    if( nu == u && nv == v ) break;
                }
            }
            lowu = min( lowu, lowv );
        } else if( pre[ v ] < pre[ u ] ){
            stk.push( pii( u, v ) ); // remember
            lowu = min( lowu, pre[ v ] );
        }
    }
    if( fa < 0 && child == 1 ) is_cut[ u ] = 0; // remember especially this
    return low[u] = lowu;
}

void tarjan(){
    bcc_cnt = dfs_clock = 0;
    memset( pre, 0, sizeof(pre) );
    memset( bccno, 0, sizeof(bccno) );
    memset( is_cut, 0, sizeof(is_cut) );
    memset( low, 0, sizeof(low) );
    dfs( 0, -1 ); // cc
}

void solve(){
    tarjan();
    ll ans_cnt = 0LL, ans_ways = 1LL;
    for(int i = 1; i <= bcc_cnt; ++i){
        int cut_cnt = 0;
        for(int j: bcc[i])
            if( is_cut[ j ] )
                ++cut_cnt;
        if( cut_cnt == 1 )
            ++ans_cnt, ans_ways *= (ll)( bcc[i].size() - 1 );
    }
    if( bcc_cnt == 1 )
        ans_cnt = 2, ans_ways = bcc[1].size() * ( bcc[1].size() - 1 ) / 2;
    printf("Case %d: %lld %lld\n", ++kase, ans_cnt, ans_ways);
}

int main(){
    while( scanf("%d", &n) == 1 && n ){
        init();
        ID id;
        for(int i = 0; i < n; ++i){
            int a, b; scanf("%d%d", &a, &b);
            a = id.get( a ), b = id.get( b );
            G[a].push_back( b );
            G[b].push_back( a );
        }
        n = id.cnt;
        solve();
    }
    return 0;
}