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...