HDU 5409 雙連通縮點

2021-07-04 19:23:47 字數 3865 閱讀 4143

hdu 5409

題意:給乙個圖一些邊,保證圖連通

問對於每條邊,如果去除該邊後使得圖中一些點不連通。設這些點(u,v

),要求使

u盡量小,

v盡量大,輸出這樣的(u,

v)。否則輸出

0 0。

思路:感謝

基本的思路就是找橋,然後橋兩端找找對應的最大u

和最小v。

自己寫的時候寫完找橋就不會了,而且找橋找的特別菜~

有乙個思路,即找出兩邊最大的u

,設為u1和u2

。則u = min(u1

,u2),v = u+1。

證明:去掉橋以後,原圖變成兩個連通圖。因為u

越大越好所以取兩個圖中的最大 點,因為

v必須大於u所以

u只能取兩個連通圖中更小的u。設

u1。則在 第二個連通圖中,一定能找到乙個點

u+1滿足

v最小。否則能用

u+1來更新

u1,使得

u1更大。

因為對於每個橋割開的連通圖內,只需要表現它最大點u

的性質,所以可以強連通縮點, 最後再在縮成的「點」之間連橋。這樣原圖就變成一棵樹,然後就變成樹的遍歷問題,

加個回溯就可以求出u,v

。 縮點主要是給每個點設了個id

值,表示它屬於哪乙個新點。然後把需要的性質傳給新 點就可以。

原始碼:#include 

#include 

#include 

#include 

#include 

#include 

#include 

#include 

using namespace std;

#define gmin(a,b) ((a) < (b) ? (a) : (b))

#define gmax(a,b) ((a) > (b) ? (a) : (b))

const int maxn = 1e5 + 5;

typedef pairpii;

vectorlin[maxn];

int n, m;

int bridge[maxn];

int u[maxn], v[maxn];

int low[maxn], pre[maxn], clock;

int newid[maxn], vis[maxn], ids;

int maxu[maxn], ans[maxn];

void edge_init()

for(int i = 1 ; i <= n ; i++)

lin[i].clear();

int u, v;

for(int i = 1 ; i <= m ; i++){

scanf("%d%d", &u, &v);

lin[u].push_back(make_pair(v, i));

lin[v].push_back(make_pair(u, i));

u[i] = u, v[i] = v;

void dfs_bridge(int u, int fa)

pre[u] = low[u] = ++clock;

for(int i = 0 ; i < (int)lin[u].size() ; i++){

pii temp = lin[u][i];

int v = temp.first, id = temp.second;

if(pre[v] == 0){

dfs_bridge(v, u);

low[u] = gmin(low[u], low[v]);

if(low[v] > pre[u]) bridge[id] = 1;

else if(pre[v] != 0 && v != fa)

low[u] = gmin(low[u], pre[v]);

void dfs_shrink(int u, int fa)

newid[u] = ids;

vis[u] = 1;

for(int i = 0 ; i < (int)lin[u].size() ; i++){

int v = lin[u][i].first;

int id = lin[u][i].second;

if(!bridge[id] && v != fa && vis[v] == 0){

dfs_shrink(v, u);

void bcc_bridge()

memset(bridge, 0, sizeof(bridge));

memset(pre, 0, sizeof(pre));

clock = 0;

dfs_bridge(1, -1);

void shrink()

memset(vis, 0, sizeof(vis));

ids = 0;

for(int i = 1 ; i <= n ; i++){

if(vis[i] == 0){

++ids;

dfs_shrink(i, -1);

for(int i = 1 ; i <= n ; i++)

lin[i].clear();

for(int i = 1 ; i <= m ; i++){

if(bridge[i]){

int u = newid[u[i]], v = newid[v[i]];

lin[u].push_back(make_pair(v, i));

lin[v].push_back(make_pair(u, i));

void dfs(int u, int fa)

ans[u] = maxu[u];

pre[u] = ++clock;

for(int i = 0 ; i < (int)lin[u].size() ; i++){

int v = lin[u][i].first;

if(v != fa && pre[v] == 0){

dfs(v, u);

ans[u] = gmax(ans[u], ans[v]);

int main()

int t;

scanf("%d", &t);

while(t--){

scanf("%d%d", &n, &m);

edge_init();

bcc_bridge();

shrink();

//        for(int i = 1 ; i <= m ; i++)

//            printf("u[%d] = %d, v[%d] = %d, bridge = %d\n", i, u[i], i, v[i], bridge[i]);

memset(maxu, 0, sizeof(maxu));

for(int i = 1 ; i <= n ; i++)

maxu[newid[i]] = gmax(maxu[newid[i]], i);

int u;

for(u = 1 ; u <= ids ; u++)

if(maxu[u] == n)

break;

clock = 0;

memset(pre, 0, sizeof(pre));

dfs(u, -1);

for(int i = 1 ; i <= m ; i++){

if(!bridge[i]){

printf("0 0\n");

else{

int u = newid[u[i]], v = newid[v[i]];

if(pre[u] < pre[v])

swap(u, v);

printf("%d %d\n", ans[u], ans[u] + 1);

return 0;

求橋,邊雙連通縮點

乙個無向圖,問最少新增多少條邊使任意兩點有兩條不同路徑。即構造乙個邊雙連通圖,邊雙連通縮點後是一棵樹,度數為1的點為a,結論是需要新增 a 1 2條邊。include include include include include include include include include in...

POJ 3352 雙連通縮點求縮點樹葉子節點數

題意 給定n個點m條邊的無向圖 保證連通 問 至少加多少條邊可以使圖為雙連通圖 思路 雙連通圖即所有點都屬於至少乙個環中 顯然我們先把圖縮點得到一棵縮點樹,問題就轉成在縮點樹上加最少多少條邊使得圖為雙連通圖。對於n個節點的無根樹,至少要 1 left 2 條邊 left為葉子節點數 include ...

POJ 2942 點雙連通

自己試著敲試試 莫名奇妙的過了,我還是要好好研究原理。點雙聯通 每個點與其他點都至少有兩條路徑。include include include include include include include define maxn 1020 define maxm 1000010 using nam...