Network POJ-3694
Network POJ-3694
文章目錄
- Description
- 題意:
- 樣例分析:
- 題解:
- 代碼:
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 0Sample Output
Case 1: 1 0Case 2: 2 0題意:
n個點,m個邊
點與點之間通過邊連接,如果切斷某個邊使得有點與其他點連接斷開(連通分支增加),則稱這種邊為橋梁(離散上叫割邊)。
接下來有Q個操作,每操作一次,輸出當前存在的橋梁數(shù)量
樣例分析:
我們看這個
4 4
1 2
2 1
2 3
1 4
2
1 2
3 4
0 0
一開始是圖中藍色部分,其中1和4之間的邊,2和3之間的邊稱之為橋,再加入12邊后(綠色),橋還是那倆沒變,再加入43邊后,橋的數(shù)量為0(因為此時你去掉任何一個邊,任意兩個點都可連接)
題解:
題解借鑒:
題解一
題解二
本人對lca的講解
我又感覺并查集可以做(雖然我并沒有嘗試)
Tarjan縮點問題
運行一次tarjan,求出橋和縮點,那么無向圖將縮點組成一個數(shù),樹邊就是原本的橋。我們想要得到一顆有根樹,那我們就可以在執(zhí)行tarjan的時候記錄每一個點父節(jié)點和深度即可
每次連接兩個點,如果兩個點屬于同一個縮點,那連接后不會產(chǎn)生影響,但如果不屬于一個縮點的兩點u和v,連接后,我們就要找這兩個點的lca,即最小公共祖先(這也是我們要得到有根樹的原因),這樣u->lca(u,v)->v->u將連成一個環(huán),而里面的樹邊也不再是橋,路徑上的點都可以縮成一個點,即合成一個分量
對于縮點的處理:
縮點相當于在一個強連通分量里的點的集合,我們在實際操作中,有兩個方法使用縮點
一個是:選取一個點為實點,其余點為虛點。實點即代表著這個分量的所有信息,虛點雖然屬于這個分量的點,但可以直接無視。我們要做的,就是在這個分量里選擇一個點,去代表整個分量。(相當于以一個代全部)
另一方法是:對每個點都設(shè)置一個歸屬集合,即表示這個點屬于哪一個集合。在處理的過程中,一個集合可能又會被另一個集合所包含,所以我們可以利用并查集的路徑壓縮,很快地找到一個點的最終所屬集合。(想當于一個整體)
代碼:
方法一:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #include <string> #include <set> #define ms(a,b) memset((a),(b),sizeof((a))) using namespace std; typedef long long LL; const double EPS = 1e-8; const int INF = 2e9; const LL LNF = 2e18; const int MAXN = 1e5+10;struct Edge {int to, next; }edge[MAXN*8]; int tot, head[MAXN];int index, dfn[MAXN], low[MAXN]; int isbridge[MAXN], sum_bridge; int fa[MAXN], depth[MAXN];void addedge(int u, int v) {edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++; }void Tarjan(int u, int pre) {dfn[u] = low[u] = ++index;depth[u] = depth[pre] + 1; //記錄深度fa[u] = pre; //記錄父親結(jié)點for(int i = head[u]; i!=-1; i = edge[i].next){int v = edge[i].to;if(v==pre) continue;if(!dfn[v]){Tarjan(v, u);low[u] = min(low[u], low[v]);if(low[v]>dfn[u]) //isbridge[v]表示在樹中,以v為兒子結(jié)點的邊是否為橋isbridge[v] = 1, sum_bridge++;}elselow[u] = min(low[u], dfn[v]);} }void LCA(int u, int v) {if(depth[u]<depth[v]) swap(u, v);while(depth[u]>depth[v]) //深度大的先往上爬。遇到橋,就把它刪去。{if(isbridge[u]) sum_bridge--, isbridge[u] = 0;u = fa[u];}while(u!=v) //當深度一樣時,一起爬。遇到橋,就把它刪去。{if(isbridge[u]) sum_bridge--, isbridge[u] = 0;u = fa[u];if(isbridge[v]) sum_bridge--, isbridge[v] = 0;v = fa[v];} }void init() {tot = 0;memset(head, -1, sizeof(head));index = 0;memset(dfn, 0, sizeof(dfn));memset(low, 0, sizeof(low));memset(isbridge, 0, sizeof(isbridge));sum_bridge = 0; }int main() {int n, m, kase = 0;while(scanf("%d%d", &n, &m) && (n||m) ){init();for(int i = 1; i<=m; i++){int u, v;scanf("%d%d", &u, &v);addedge(u, v);addedge(v, u);}depth[1] = 0;Tarjan(1, 1);int q, a, b;scanf("%d", &q);printf("Case %d:\n", ++kase);while(q--){scanf("%d%d", &a, &b);LCA(a, b);printf("%d\n", sum_bridge);}printf("\n");} }方法二:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #include <string> #include <set> #define ms(a,b) memset((a),(b),sizeof((a))) using namespace std; typedef long long LL; const double EPS = 1e-8; const int INF = 2e9; const LL LNF = 2e18; const int MAXN = 1e6+10;struct Edge {int to, next; }edge[MAXN], edge0[MAXN]; //edge為初始圖, edge0為重建圖 int tot, head[MAXN], tot0, head0[MAXN];int index, dfn[MAXN], low[MAXN]; int top, Stack[MAXN], instack[MAXN]; int belong[MAXN]; int fa[MAXN], depth[MAXN]; //fa用于重建圖時記錄當前節(jié)點的父親節(jié)點,depth記錄當前節(jié)點的深度 int sum_bridge;//找到x最終所屬的結(jié)合 int find(int x) { return belong[x]==x?x:belong[x]=find(belong[x]); }void addedge(int u, int v, Edge edge[], int head[], int &tot) {edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++; }void Tarjan(int u, int pre) {dfn[u] = low[u] = ++index;Stack[top++] = u;instack[u] = true;for(int i = head[u]; i!=-1; i = edge[i].next){int v = edge[i].to;if(v==pre) continue;if(!dfn[v]){Tarjan(v, u);low[u] = min(low[u], low[v]);if(low[v]>dfn[u]) sum_bridge++;}else if(instack[v])low[u] = min(low[u], dfn[v]);}if(dfn[u]==low[u]){int v;do{v = Stack[--top];instack[v] = false;belong[v] = u; //把集合的編號設(shè)為聯(lián)通分量的第一個點}while(v!=u);} }void build(int u, int pre) {fa[u] = pre; //記錄父親節(jié)點depth[u] = depth[pre] + 1; //記錄深度for(int i = head0[u]; i!=-1; i=edge0[i].next)if(edge0[i].to!=pre) //防止往回走build(edge0[i].to, u); }int LCA(int u, int v) //左一步右一步地找LCA {if(u==v) return u; //因為兩個結(jié)點一定有LCA, 所以一定有u==v的時候//可能爬一步就爬了幾個深度,因為中間的結(jié)點已經(jīng)往上縮點了if(depth[u]<depth[v]) swap(u, v); //深度大的往上爬sum_bridge--;int lca = LCA(find(fa[u]), v);return belong[u] = lca; //找到了LCA,在沿路返回的時候把當前節(jié)點的所屬集合置為LCA的所屬集合 }void init() {tot = tot0 = 0;memset(head, -1, sizeof(head));memset(head0, -1, sizeof(head0));index = top = 0;memset(dfn, 0, sizeof(dfn));memset(low, 0, sizeof(low));memset(instack, 0, sizeof(instack));sum_bridge = 0; }int main() {int n, m, kase = 0;while(scanf("%d%d", &n, &m) && (n||m) ){init();for(int i = 1; i<=m; i++){int u, v;scanf("%d%d", &u, &v);addedge(u, v, edge, head, tot);addedge(v, u, edge, head, tot);}Tarjan(1, 1);for(int u = 1; u<=n; u++) //重建建圖for(int i = head[u]; i!=-1; i = edge[i].next){int tmpu = find(u);int tmpv = find(edge[i].to);if(tmpu!=tmpv)addedge(tmpu, tmpv, edge0, head0, tot0);}depth[find(1)] = 0;build(find(1), find(1)); //把無根樹轉(zhuǎn)為有根樹int q, a, b;scanf("%d", &q);printf("Case %d:\n", ++kase);while(q--){scanf("%d%d", &a, &b);LCA(find(a), find(b));printf("%d\n", sum_bridge);}printf("\n");} }總結(jié)
以上是生活随笔為你收集整理的Network POJ-3694的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国外装饰公司是怎么做的(国外有装修公司吗
- 下一篇: HDU4612 Warm up