標籤:最佳化 any lis cpp single namespace cal memory 標記
Network
| Time Limit: 5000MS |
|
Memory Limit: 65536K |
| Total Submissions: 9434 |
|
Accepted: 3511 |
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 21 22 321 21 34 41 22 12 31 421 23 40 0
Sample Output
Case 1:10Case 2:20
Source
2008 Asia Hefei Regional Contest Online by USTC、原題大意:輸入焦點的個數N和邊的個數M,接下來輸入整數Q代表詢問的次數,之後Q行有兩個整數A,B,表示將A與B相連。問每次詢問後割邊(橋)的數量。解題思路1:用tarjian找割邊並標記,之後用lca遍曆A與B到最近公用祖先的路徑,這段路上是割邊的處理掉,總數-1就可以了。 表示用鏈表寫的,各種慢,但是POJ資料太弱了,怎麼寫都能過。
#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;struct list { int v; list *next; };list *head[110010],*rear[110010];int father[110010],times,dfn[110010],low[110010],bridge[110010],bridgenum,n;int dep[110010];void init() { int i; memset(head,0,sizeof(head)); memset(dep,0,sizeof(dep)); memset(rear,0,sizeof(rear)); for(i=1;i<=n;++i) father[i]=i; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(bridge,0,sizeof(bridge)); times=bridgenum=0; }void insert(int a,int b) { if(rear[a]!=NULL) { rear[a]->next=new list; rear[a]=rear[a]->next;} else head[a]=rear[a]=new list;rear[a]->v=b;rear[a]->next=NULL;return; }void tarjian(int v) { bool flag=true; dfn[v]=low[v]=++times; dep[v]=dep[father[v]]+1; for(list *p=head[v];p!=NULL;p=p->next) { if(p->v==father[v]&&flag) { flag=false; continue;} if(!dfn[p->v]) { father[p->v]=v; tarjian(p->v); low[v]=min(low[v],low[p->v]); if(low[p->v]>dfn[v]) { ++bridgenum; bridge[p->v]=1; }}else low[v]=min(low[v],dfn[p->v]); } }void lca(int a,int b) { if(dep[a]<dep[b]) swap(a,b); while(dep[a]!=dep[b]) { if(bridge[a]) { bridge[a]=0; --bridgenum; }a=father[a]; } while(a!=b) { if(bridge[a]) { bridge[a]=0; --bridgenum; }if(bridge[b]) { bridge[b]=0; --bridgenum; }a=father[a];b=father[b]; } return; }int main() { int m,i,a,b,q,ccase=0; while(~scanf("%d%d",&n,&m),n&&m) { init(); for(i=0;i<m;++i) { scanf("%d%d",&a,&b); insert(a,b);insert(b,a);} tarjian(1); scanf("%d",&q); printf("Case %d:\n",++ccase); for(i=1;i<=q;++i) { scanf("%d%d",&a,&b); lca(a,b); printf("%d\n",bridgenum);} } return 0; }
解題思路2:在原來的基礎上用並查集最佳化,這種做法是看了大神們的解題思路寫出的。
將雙連通的兩個點弄成一個集合,這樣在LCA時只要判斷是否是一個集合,將橋的數量減去即可。
#include<stdio.h>#include<string.h>struct list { int v; list *next; };list *head[111010],*rear[111010];int n,m,father[111010],dep[110010],low[110010],fath[110010],bridgenum;void init() { int i; memset(head,0,sizeof(head)); memset(rear,0,sizeof(rear)); memset(dep,0,sizeof(dep)); memset(low,0,sizeof(low)); memset(fath,0,sizeof(fath)); for(i=1;i<=n;++i) father[i]=i; bridgenum=0; }void insert(int a,int b) { if(rear[a]!=NULL) { rear[a]->next=new list; rear[a]=rear[a]->next; } else head[a]=rear[a]=new list; rear[a]->v=b; rear[a]->next=NULL; }int find(int x) { return father[x]==x?x:father[x]=find(father[x]); }void merge(int x,int y) { int fx=find(x); int fy=find(y); if(fx!=fy) father[fx]=fy; }void tarjian(int v,int deps) { bool flag=true; dep[v]=low[v]=deps; for(list *p=head[v];p!=NULL;p=p->next) { if(p->v==fath[v]&&flag) { flag=false; continue;} if(!dep[p->v]) { fath[p->v]=v; tarjian(p->v,deps+1); if(low[v]>low[p->v]) low[v]=low[p->v]; if(low[p->v]<=dep[v]) merge(p->v,v); else bridgenum++;} else if(low[v]>dep[p->v]) low[v]=dep[p->v]; } }void judge(int v) { int x=find(v); int y=find(fath[v]); if(x!=y) { --bridgenum; father[x]=y; } }void lca(int u,int v) { while(dep[u]>dep[v]) { judge(u); u=fath[u];} while(dep[u]<dep[v]) { judge(v); v=fath[v];} while(u!=v) { judge(u);judge(v); u=fath[u];v=fath[v];} }int main() { int a,b,q,num=0; while(~scanf("%d%d",&n,&m)) { if(n==0&&m==0) break; init(); while(m--) { scanf("%d%d",&a,&b); insert(a,b);insert(b,a);} printf("Case %d:\n",++num); tarjian(1,1); scanf("%d",&q); while(q--) { scanf("%d%d",&a,&b); if(find(a)!=find(b)) lca(a,b); printf("%d\n",bridgenum);} printf("\n"); } return 0; }
[雙連通分量] POJ 3694 Network