POJ 3694 Network
双连通分量 targan
传送门:HustOJ
传送门:POJ
题意
给你一个连通无向图,10w点。有Q次查询,每次查询a,b,先在ab间连一条路,再问全图剩下多少桥。
思路
这题可以用targan求出双连通分量,重新建图,用并查集维护连通块。不过很麻烦,每次加边时合并谁是个问题。
感觉这个人讲的很清楚。
实际上,这道题也不一定要缩点,如果用缩点的思路来做的话,程序将十分麻烦。可以直接根据dfn值来进行LCA。因为,我们观察low[v] > dfn[u]这个条件,代表的意思就是v无法通过回边或者通过子女到达比u点更靠前的点,那么我们只需要标记v点即可表明割边。在进行LCA时,由于树的组成就是原图中的割边,所以在原图中,根据这个标记来判断是否将割边被转化为了普通边。
我再解释一下,就是targan的dfs时,求出每个点的爸爸,然后如果low[to]>dfn[n],就表明to以及to的后代回不到n以前,那么to和n的边就是桥。标记一下to点就行了。
然后求LCA时,用最普通的方法,往爸爸上跳就行,跳的过程中发现了桥,那么这个桥就不是桥了,cnt–。因为targan后变成了一棵树,即使我们没有实际缩点,每个点都有了父亲,图实际已经变成一棵树了。将树的两个叶子连起来,路上所有点自然会成环,桥就不存在了。
代码
4000ms+卡过
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#define _ ios_base::sync_with_stdio(0),cin.tie(0)
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=100005;
const int oo=0x3f3f3f3f;
typedef long long LL;
const LL loo=4223372036854775807ll;
typedef long double LB;
const LL mod=1e9+7;
int father[MAXN];
int low[MAXN], dfn[MAXN], isbridge[MAXN], lev[MAXN];
vector<int> G[MAXN];
int dfs_clock=0;
int cnt=0;
void targan(int n)
{
low[n]=dfn[n]=++dfs_clock;
lev[n]=lev[father[n]]+1;
for(int i=0;i<G[n].size();i++)
{
int to=G[n][i];
if(!dfn[to])
{
father[to]=n;
targan(to);
low[n]=min(low[n], low[to]);
if(low[to]>dfn[n])//to以及to的后代 回不去n之前的点
{
cnt++;
isbridge[to]=1;
}
}
else if(to!=father[n])
{
low[n]=min(low[n], dfn[to]);
}
}
}
void LCA(int a, int b)
{
while(lev[a]>lev[b])
{
if(isbridge[a]) cnt--, isbridge[a]=0;
a=father[a];
}
while(lev[a]<lev[b])
{
if(isbridge[b]) cnt--, isbridge[b]=0;
b=father[b];
}
while(a!=b)
{
if(isbridge[a]) cnt--, isbridge[a]=0;
a=father[a];
if(isbridge[b]) cnt--, isbridge[b]=0;
b=father[b];
}
}
int main()
{
_;
int Case=0;
int n;
while(cin>>n)
{
M(father, 0);M(low, 0);M(dfn, 0);M(isbridge, 0);M(lev, 0);
for(int i=0;i<MAXN;i++) G[i].clear();
cnt=dfs_clock=0;
int m;cin>>m;
if(n==0&&m==0) break;
cout<<"Case "<<++Case<<":"<<endl;
while(m--)
{
int a, b;cin>>a>>b;
G[a].push_back(b);G[b].push_back(a);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])
targan(i);
}
int q;cin>>q;
while(q--)
{
int a, b;cin>>a>>b;
LCA(a, b);
cout<<cnt<<endl;
}
cout<<endl;
}
return 0;
}