Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 6942 | Accepted: 2476 |
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求桥的个数也能过。
缩完点后图一定是树。以这棵树的任意一个节点为根节点,遍历整棵树,记录每个节点的父亲节点,并进行预处理,在询问的时候求LCA。
对于每次询问:(a,b)
求出a,b的最近公共祖先c。依次统计当前的图a到c的边中有多少条桥边,b到c的边中有多少条桥边。统计完以后,由于a到c的边,b到c的边已经不是桥边,我们可以用 f[i] 记录第i个点到它深度最小的祖先,且满足它到该祖先没有桥边。那么询问的总复杂度为O(N)。
代码如下:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<queue>
#include<stack>
#include<vector>
#include<math.h>
#include<stdlib.h>
#define nn 110000
#define inff 0x3fffffff
using namespace std;
int n,m;
struct node
{
int st,en,next;
}E[nn*10];
int p[nn],num;
void init()
{
memset(p,-1,sizeof(p));
num=0;
}
void add(int st,int en)
{
E[num].st=st;
E[num].en=en;
E[num].next=p[st];
p[st]=num++;
}
int dfn[nn],low[nn];
int df;
bool qiao[4*nn];
int clo[nn];
int cnt;
int fa[nn];
bool use[nn];
vector<int>ve;
vector<int>tu[nn];
void dfs(int id,int pre)
{
dfn[id]=low[id]=++df;
int i,w;
for(i=p[id];i+1;i=E[i].next)
{
w=E[i].en;
if((i^1)==pre)
continue;
if(dfn[w]==-1)
{
dfs(w,i);
if(low[w]>dfn[id])
{
ve.push_back(i);
qiao[i]=qiao[i^1]=true;
}
low[id]=min(low[id],low[w]);
}
else
low[id]=min(low[id],dfn[w]);
}
}
void go(int id)
{
clo[id]=cnt;
int i,w;
for(i=p[id];i+1;i=E[i].next)
{
if(qiao[i])
continue;
w=E[i].en;
if(clo[w]==-1)
go(w);
}
}
int dep[nn*2];
int vis;
int pos[nn];
int fang[nn*2];
int dp[nn*2][20];
void DFS(int id,int pre,int shen)
{
fa[id]=pre;
dep[++vis]=shen;
fang[vis]=id;
pos[id]=vis;
int i,w;
for(i=p[id];i+1;i=E[i].next)
{
w=E[i].en;
if(w==pre)
continue;
DFS(w,id,shen+1);
dep[++vis]=shen;
fang[vis]=id;
}
}
void rmq_init()
{
int i,j;
for(i=1;i<=vis;i++)
{
dp[i][0]=i;
}
for(j=1;(1<<j)<=vis;j++)
{
for(i=1;i+(1<<j)-1<=vis;i++)
{
if(dep[dp[i][j-1]]<dep[dp[i+(1<<(j-1))][j-1]])
dp[i][j]=dp[i][j-1];
else
dp[i][j]=dp[i+(1<<(j-1))][j-1];
}
}
}
void solve()
{
int i;
memset(dfn,-1,sizeof(dfn));
df=0;
memset(qiao,false,sizeof(qiao));
ve.clear();
dfs(1,-1);
memset(clo,-1,sizeof(clo));
cnt=0;
for(i=1;i<=n;i++)
{
if(clo[i]==-1)
{
cnt++;
go(i);
}
}
int ix,u,v;
init();
sort(ve.begin(),ve.end());
for(i=0;i<(int)ve.size();i++)
{
ix=ve[i];
u=clo[E[ix].st],v=clo[E[ix].en];
add(u,v);
add(v,u);
}
vis=0;
DFS(1,1,0);
rmq_init();
}
int rmq(int l,int r)
{
if(l>r)
swap(l,r);
int k=0;
while((1<<(k+1))<r-l+1)
{
k++;
}
if(dep[dp[l][k]]<dep[dp[r-(1<<k)+1][k]])
return dp[l][k];
return dp[r-(1<<k)+1][k];
}
int main()
{
int i,q;
int u,v;
int cas=1;
while(scanf("%d%d",&n,&m)&&n+m)
{
init();
for(i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
solve();
scanf("%d",&q);
int ix,fc;
int nq=ve.size();
printf("Case %d:\n",cas++);
memset(use,false,sizeof(use));
while(q--)
{
scanf("%d%d",&u,&v);
u=clo[u],v=clo[v];
ix=rmq(pos[u],pos[v]);
ix=fang[ix];
while(dep[pos[u]]>dep[pos[ix]])
{
if(!use[u])
{
use[u]=true;
nq--;
}
fc=u;
u=fa[u];
if(dep[pos[u]]>dep[pos[ix]])
fa[fc]=ix;
}
while(dep[pos[v]]>dep[pos[ix]])
{
if(!use[v])
{
use[v]=true;
nq--;
}
fc=v;
v=fa[v];
if(dep[pos[v]]>dep[pos[ix]])
fa[fc]=ix;
}
printf("%d\n",nq);
}
}
return 0;
}