HDU2460(Network)

Network

Problem Description
A network administrator manages a large network. The network consists of N computers and M links between pairs of computers. Any pair of computers are connected directly or indirectly by successive links, so data can be transformed between any two computers. The administrator finds that some links are vital to the network, because failure of any one of them can cause that data can’t be transformed between some computers. He call such a link a bridge. He is planning to add some new links one by one to eliminate all bridges.

You are to help the administrator by reporting the number of bridges in the network after each new link is added.

Input
The input consists of multiple test cases. Each test case starts with a line containing two integers N(1 ≤ N ≤ 100,000) and M(N - 1 ≤ M ≤ 200,000).
Each of the following M lines contains two integers A and B ( 1≤ A ≠ B ≤ N), which indicates a link between computer A and B. Computers are numbered from 1 to N. It is guaranteed that any two computers are connected in the initial network.
The next line contains a single integer Q ( 1 ≤ Q ≤ 1,000), which is the number of new links the administrator plans to add to the network one by one.
The i-th line of the following Q lines contains two integer A and B (1 ≤ A ≠ B ≤ N), which is the i-th added new link connecting computer A and B.

The last test case is followed by a line containing two zeros.

Output
For each test case, print a line containing the test case number( beginning with 1) and Q lines, the i-th of which contains a integer indicating the number of bridges in the network after the first i new links are added. Print a blank line after the output for each test case.

Sample Input
3 2
1 2
2 3
2
1 2
1 3
4 4
1 2
2 1
2 3
1 4
2
1 2
3 4
0 0

Sample Output
Case 1:
1
0

Case 2:
2
0

思路

給定一張無向圖,求出當前圖中所有的橋(割邊)。然後有Q次查詢,每次新加入一個對點,問加入該對點之後橋的數目是多少。

自己的思路:開始想法挺好又是縮點又是統計度數的。大概就是先跑一遍tarjan統計橋的數目,然後縮點最後統計縮點的度。開始Q次查詢操作,每次輸入兩個點,因爲縮完點了橋肯定是縮點圖中的邊。然後對輸入的兩個點進行判斷是不是屬於同一個連通分量,如果在同一個連通分量或者當前圖中橋數量爲0,這條邊加不加入都無所謂,如果不屬於同一個連通分量就加入邊重新跑tarjan計算。我算了一下複雜度大概差也差不多。最壞情況下勉勉強強,但是一直WA沒找到錯誤。

Tarjan + LCA
題解思路:跑一次Tarjan,統計橋邊數目。然後對接下來的Q次操作做LCA,想想覺得好聰明,但是我想不到網上的人一個比一個聰明。爲什麼做LCA?

  1. Tarjan跑完之後是一顆生成樹,樹中每個節點都有一個訪問時間序以及一個父節點,橋邊做標記。
  2. 可以想象加入對兩個葉節點x,y建立一條無向邊,那麼這兩個葉節點到他們最近的公共祖先 f 中所有點就成環了,消除了f - x 以及 f - y這些路上所有的橋邊。而且對於任意加入的兩個節點都可以得到想要的結果。
  3. 由於兩個節點的時間序(深度)可能不同,先讓兩個節點統一到一個深度在讓他們同時上跳,一路跳一路把橋邊標記取消,橋邊數目一直減1。當他們跳到同一個節點(最近公共祖先)結束。
  4. 橋邊雖然是u – v。但是爲了方便處理以及查詢用cut[v]標記橋,用u標記不好處理。

最後注意一下細節就可以了~

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
#pragma comment(linker,"/STACK:10240000,10240000")
using namespace std;
const int maxn = 1e5+5;
struct edge{
	int to;
	int next;
}e[maxn*4];
int head[maxn];
int dfn[maxn];
bool cut[maxn];
int low[maxn];
int f[maxn];
int tot,cnt,ans;
void clear_set()
{
	cnt = ans = tot = 0;
    memset(head,-1,sizeof(head));
	memset(dfn,0,sizeof(dfn));
    memset(cut,false,sizeof(cut));
	memset(low,0,sizeof(low));
    for(int i = 0;i < maxn;i++){
        f[i] = i;
    }
}
void addedge(int x,int y)
{
	e[tot].to = y;
	e[tot].next = head[x];
	head[x] = tot++;
}
void tarjan(int x,int fx)
{
	dfn[x] = low[x] = ++cnt;
	int k = 0;
	for(int i = head[x];~i;i = e[i].next){
		int y = e[i].to;
		if(fx == y && !k){          //重邊,跳過一次
			k++;
			continue;
		}
		if(!dfn[y]){
            f[y] = x;				//記錄父節點
			tarjan(y,x);
			low[x] = min(low[x],low[y]);
			if(low[y] > dfn[x]){
                cut[y] = true;
				ans++;
			}
		}
		else if(dfn[y] < dfn[x]){
			low[x] = min(low[x],dfn[y]);
		}
	}
}
void LCA(int x,int y)
{
	//樹根爲深度1,保持x的深度 大於 y的深度。
    if(dfn[x] < dfn[y])     swap(x,y);
    while(dfn[x] > dfn[y]){			//跳到同一個深度
        if(cut[x]){
            cut[x] = false;
            ans--;
        }
        x = f[x];
    }
    while(x != y){					//x和y齊頭並進
        if(cut[x]){ cut[x] = false;ans--;}
        if(cut[y]){ cut[y] = false;ans--;}
        x = f[x];y = f[y];
    }
}
int main()
{
	int n,m,k = 1,q;
	while(~scanf("%d%d",&n,&m)){
		if(n == 0 && m == 0)	break;
		clear_set();
		int x,y;
		for(int i = 0;i < m;i++){
			scanf("%d%d",&x,&y);
			addedge(x,y);	addedge(y,x);
		}
        tarjan(1,-1);
		printf("Case %d:\n",k++);
		scanf("%d",&q);
		while(q--){
			scanf("%d%d",&x,&y);
			LCA(x,y);
			printf("%d\n",ans);
		}
		printf("\n");
	}
	return 0;
}

願你走出半生,歸來仍是少年~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章