1. 程式人生 > >[USACO06JAN]冗餘路徑Redundant Paths --- 邊-雙聯通分量 + 縮點

[USACO06JAN]冗餘路徑Redundant Paths --- 邊-雙聯通分量 + 縮點

**傳送門:**洛谷P2860


題目大意

給出一張無向圖(保證聯通),問至少新增多少條邊使得整個圖為邊雙聯通圖(即不存在橋)


分析

首先求出原圖的e-DCC(邊雙聯通分量),將其縮成一個點.此時,原圖成了一棵樹,給定一個結論:若縮點後的圖中度為1的節點數量為tot,則需要新增 t o t

2 \lceil \frac{tot}{2} \rceil 條邊( 即為 ( t o t
+ 1 ) > > 1 (tot + 1) >> 1
)
  求出原圖的e-DCC:求出所有的橋後,將其標記,第二次遍歷時不訪問橋,則每一次便利的點集均為一個e-DCC.注意存在重邊的處理.

  縮點:方法比較多,亂搞就好 


程式碼

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stack>

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1; 
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

int n, m;

int to[20005], nxt[20005];
int last[5005];
int cnt = -1;
IL void add(int u, int v)
{
    to[++cnt] = v; nxt[cnt] = last[u]; last[u] = cnt;
    to[++cnt] = u; nxt[cnt] = last[v]; last[v] = cnt; 
}

int dfn[5005], low[5005];
bool is_bridge[20005];
int col, belong[5005];

int degree[5005];
stack<int> bridge;


IL int min_(int x, int y) { return x < y ? x : y; }

IL void tarjan(int u, int pre)
{
    dfn[u] = low[u] = ++cnt;
    for(int i = last[u], v; i != -1; i = nxt[i])
    if((i ^ pre) != 1)
    {
        v = to[i];
        if(!dfn[v])
        {
            tarjan(v, i);
            low[u] = min_(low[u], low[v]);
            
            if(low[v] > dfn[u])
            {
                is_bridge[i] = is_bridge[i ^ 1] = 1;
                bridge.push(i);
            }
        }else
            low[u] = min_(low[u], dfn[v]);
    }
}

IL void dfs(int u, int k)
{
    belong[u] = k;
    for(int i = last[u]; i != -1; i = nxt[i])
    if(!is_bridge[i] && !belong[to[i]])
        dfs(to[i], k);
}

int main()
{
    n = read(); m = read();
    memset(last, -1, sizeof(last));
    for(int i = 1, x; i <= m; ++i)
    {
    	x = read();
    	add(x, read());
    }
    cnt = 0; tarjan(1, -1);
    
    for(int i = 1; i <= n; ++i)
    if(!belong[i])
    	dfs(i, ++col);
    
    for(int t = bridge.size(), x; t; --t)
    {
    	x = bridge.top(); bridge.pop();
    	++degree[belong[to[x]]]; ++degree[belong[to[x ^ 1]]];
    }
    
    int tot = 0;
    for(int i = 1; i <= col; ++i)
    if(degree[i] == 1)
    	++tot;
    
    printf("%d\n", (tot + 1) >> 1);
    
    return 0;
}