pku3694 Network
题意是给出一个连通无向图,求每次加一条边后,图内割边的数目。
最容易想到的方法是每加一条边都做一次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;
}