题意是给出一个连通无向图,求每次加一条边后,图内割边的数目。
最容易想到的方法是每加一条边都做一次DFS求割边,于是code之,提交,TLE。
然后上网搜了一下,看到了一个更直观的方法:设新加入的边为(u,v),先求u和v的LCA,看从LCA分别到u和v的路径上有多少条割边,然后从原图的割边数目上累减,结果就是所求,因为每加一条边,该边与DFS树上的边形成了环,环内的边就不再是割边了。这样只需要做一次DFS。
#include
<
iostream
>
using namespace std;
#define MAXN 100001
#define Min(a,b) (a<b?a:b)
int p[MAXN],ecnt,n,m,q,T = 1 ,lowlink[MAXN],dfn[MAXN],sign,cnt,odeg[MAXN],pnt[MAXN],cutp[MAXN];
bool cut[MAXN],visited[MAXN];
struct Edge{
int v,next;
}edg[ 10 * MAXN];
void init(){
ecnt = 0 ;
memset(p, - 1 , sizeof (p));
sign = 0 ;
memset(dfn, - 1 , sizeof (dfn));
memset(odeg, 0 , sizeof (odeg));
cnt = 0 ;
memset(pnt, - 1 , sizeof (pnt));
memset(cut, false , sizeof (cut));
memset(cutp, - 1 , sizeof (cutp));
}
void dfs( int pre, int u){
int i,v;
lowlink[u] = dfn[u] =++ sign;
for (i = p[u];i !=- 1 ;i = edg[i].next){
v = edg[i].v;
odeg[u] ++ ;
if (dfn[v] !=- 1 ){
if (v != pre) //
lowlink[u] = Min(lowlink[u],dfn[v]);
}
else {
dfs(u,v);
pnt[v] = u;
lowlink[u] = Min(lowlink[u],lowlink[v]);
if (dfn[u] < lowlink[v]){
cnt ++ ;
cut[u] = true ;
// 存割边
edg[ecnt].next = cutp[u];
edg[ecnt].v = v;
cutp[u] = ecnt ++ ;
}
}
}
}
void fun( int u, int v){ // 找LCA,求LCA分别到u和v的路径上的割边数目,比较暴力
int t,pt;
bool find;
memset(visited, false , sizeof (visited));
int x = pnt[u],y = pnt[v],LCA = 0 ;
// 找LCA
while (x !=- 1 ){
visited[x] = true ;
x = pnt[x];
}
while (y !=- 1 ){
if (visited[y]){
LCA = y;
break ;
}
y = pnt[y];
}
if (LCA == 0 )
LCA = 1 ;
x = u,y = v;
do {
if (cut[pnt[x]]){ // 若x的双亲是某一割边的起点,则(pnt[x],x)有可能是一条割边
find = false ;
pt = t = cutp[pnt[x]];
while (t !=- 1 ){ // 看x是不是属于以pnt[x]为起点的割边的终点
if (edg[t].v == x){
find = true ;
if (pt == t) // 找到则从终点集中去掉x
cutp[pnt[x]] = edg[t].next;
else
edg[pt].next = edg[t].next;
break ;
}
pt = t;
t = edg[t].next;
}
if (find)
cnt -- ;
if (cutp[pnt[x]] ==- 1 ) // 终点集为空,则pnt[x]不再是割边的起点
cut[pnt[x]] = false ;
}
x = pnt[x];
} while (x != LCA && x !=- 1 );
do {
if (cut[pnt[y]]){
find = false ;
pt = t = cutp[pnt[y]];
while (t !=- 1 ){
if (edg[t].v == y){
find = true ;
if (pt == t)
cutp[pnt[y]] = edg[t].next;
else
edg[pt].next = edg[t].next;
break ;
}
pt = t;
t = edg[t].next;
}
if (find)
cnt -- ;
if (cutp[pnt[y]] ==- 1 )
cut[pnt[y]] = false ;
}
y = pnt[y];
} while (y != LCA && y !=- 1 );
}
int main(){
int i,u,v;
while (scanf( " %d%d " , & n, & m) && n && m){
init();
for (i = 0 ;i < m;i ++ ){
scanf( " %d%d " , & u, & v);
edg[ecnt].next = p[u];
edg[ecnt].v = v;
p[u] = ecnt ++ ;
edg[ecnt].next = p[v];
edg[ecnt].v = u;
p[v] = ecnt ++ ;
}
dfs( - 1 , 1 );
scanf( " %d " , & q);
printf( " Case %d:\n " ,T ++ );
for (i = 0 ;i < q;i ++ ){
scanf( " %d%d " , & u, & v);
fun(u,v);
printf( " %d\n " ,cnt);
}
printf( " \n " );
}
return 0 ;
}
using namespace std;
#define MAXN 100001
#define Min(a,b) (a<b?a:b)
int p[MAXN],ecnt,n,m,q,T = 1 ,lowlink[MAXN],dfn[MAXN],sign,cnt,odeg[MAXN],pnt[MAXN],cutp[MAXN];
bool cut[MAXN],visited[MAXN];
struct Edge{
int v,next;
}edg[ 10 * MAXN];
void init(){
ecnt = 0 ;
memset(p, - 1 , sizeof (p));
sign = 0 ;
memset(dfn, - 1 , sizeof (dfn));
memset(odeg, 0 , sizeof (odeg));
cnt = 0 ;
memset(pnt, - 1 , sizeof (pnt));
memset(cut, false , sizeof (cut));
memset(cutp, - 1 , sizeof (cutp));
}
void dfs( int pre, int u){
int i,v;
lowlink[u] = dfn[u] =++ sign;
for (i = p[u];i !=- 1 ;i = edg[i].next){
v = edg[i].v;
odeg[u] ++ ;
if (dfn[v] !=- 1 ){
if (v != pre) //
lowlink[u] = Min(lowlink[u],dfn[v]);
}
else {
dfs(u,v);
pnt[v] = u;
lowlink[u] = Min(lowlink[u],lowlink[v]);
if (dfn[u] < lowlink[v]){
cnt ++ ;
cut[u] = true ;
// 存割边
edg[ecnt].next = cutp[u];
edg[ecnt].v = v;
cutp[u] = ecnt ++ ;
}
}
}
}
void fun( int u, int v){ // 找LCA,求LCA分别到u和v的路径上的割边数目,比较暴力
int t,pt;
bool find;
memset(visited, false , sizeof (visited));
int x = pnt[u],y = pnt[v],LCA = 0 ;
// 找LCA
while (x !=- 1 ){
visited[x] = true ;
x = pnt[x];
}
while (y !=- 1 ){
if (visited[y]){
LCA = y;
break ;
}
y = pnt[y];
}
if (LCA == 0 )
LCA = 1 ;
x = u,y = v;
do {
if (cut[pnt[x]]){ // 若x的双亲是某一割边的起点,则(pnt[x],x)有可能是一条割边
find = false ;
pt = t = cutp[pnt[x]];
while (t !=- 1 ){ // 看x是不是属于以pnt[x]为起点的割边的终点
if (edg[t].v == x){
find = true ;
if (pt == t) // 找到则从终点集中去掉x
cutp[pnt[x]] = edg[t].next;
else
edg[pt].next = edg[t].next;
break ;
}
pt = t;
t = edg[t].next;
}
if (find)
cnt -- ;
if (cutp[pnt[x]] ==- 1 ) // 终点集为空,则pnt[x]不再是割边的起点
cut[pnt[x]] = false ;
}
x = pnt[x];
} while (x != LCA && x !=- 1 );
do {
if (cut[pnt[y]]){
find = false ;
pt = t = cutp[pnt[y]];
while (t !=- 1 ){
if (edg[t].v == y){
find = true ;
if (pt == t)
cutp[pnt[y]] = edg[t].next;
else
edg[pt].next = edg[t].next;
break ;
}
pt = t;
t = edg[t].next;
}
if (find)
cnt -- ;
if (cutp[pnt[y]] ==- 1 )
cut[pnt[y]] = false ;
}
y = pnt[y];
} while (y != LCA && y !=- 1 );
}
int main(){
int i,u,v;
while (scanf( " %d%d " , & n, & m) && n && m){
init();
for (i = 0 ;i < m;i ++ ){
scanf( " %d%d " , & u, & v);
edg[ecnt].next = p[u];
edg[ecnt].v = v;
p[u] = ecnt ++ ;
edg[ecnt].next = p[v];
edg[ecnt].v = u;
p[v] = ecnt ++ ;
}
dfs( - 1 , 1 );
scanf( " %d " , & q);
printf( " Case %d:\n " ,T ++ );
for (i = 0 ;i < q;i ++ ){
scanf( " %d%d " , & u, & v);
fun(u,v);
printf( " %d\n " ,cnt);
}
printf( " \n " );
}
return 0 ;
}