与poj3352恰好相反,poj3352是求加多少边是桥不存在,poj3694是求加边后还剩多少桥。
利用poj3352的思想,我自己构思了一个算法,恰好总结了这几天学的东西。
分析题意,若加的边属于某边双连通分支,肯定不会减少桥的数目,而桥肯定是树边,它会将一棵子树与祖先割开,若加边(u,v),那么,u和v所在链将成为一个环,此环上必不存在桥,因此,每添加一条边,从u,v跑上lca将桥标记掉,并修改答案。
算法流程:
因为要求多次lca,干脆转成rmq,每次o(1)即可求出。
1、tarjan求桥,顺便将lca转成欧拉序列。
2、rmq初始化,用o(nlogn)时间,此后只需用o(1)时间求lca。
3、dfs求边双连通,并查集缩点。
4、在线回答问题,若u,v在同一集合,则无需修改答案,否则求出lca后,直接爬坡修改答案,顺便用并查集。
const maxn=200000;maxm=700000;max=1073741819; var n,m,i,ans,time,len,ss,lca,lca1:longint; low,tail,b,pos,root,roof:array[1..maxn]of longint; rel:array[0..maxn]of longint; next,sora,ob:array[1..maxm]of longint; pow:array[1..maxm]of boolean; v:array[1..maxn]of boolean; d:array[0..20]of longint; f:array[1..maxn,0..20]of longint; function min(x,y:longint):longint; begin if x<y then exit(x) else exit(y) end; procedure tarjan(x,y:longint); var i,ne:longint; begin inc(time);rel[x]:=time;low[x]:=time; inc(len);f[len,0]:=x;pos[x]:=len; root[x]:=y; i:=x; while next[i]<>0 do begin i:=next[i];ne:=sora[i]; if (rel[ne]<max)and(i<>ob[roof[x]]) then low[x]:=min(low[x],rel[ne]) else if ne<>y then begin roof[ne]:=i; tarjan(ne,x); inc(len);f[len,0]:=x; low[x]:=min(low[x],low[ne]); if low[ne]>rel[x] then begin pow[i]:=false;inc(ans) end end end end; function find(x:longint):longint; begin if b[x]<>x then b[x]:=find(b[x]);find:=b[x] end; function minl(x,y:longint):longint; begin if rel[x]<rel[y] then exit(x) else exit(y) end; procedure lcr; var i,j,k:longint; begin fillchar(d,sizeof(d),0); k:=trunc(ln(len)/ln(2)); d[0]:=1; for i:=1 to k do d[i]:=d[i-1]<<1; for j:=1 to k do for i:=1 to len do if i+d[j]-1<=len then f[i,j]:=minl(f[i,j-1],f[i+d[j-1],j-1]) else break end; procedure dfs(x,y:longint); var i,ne,fa:longint; begin v[x]:=false;fa:=find(x); if fa<>y then b[fa]:=y; i:=x; while next[i]<>0 do begin i:=next[i];ne:=sora[i]; if v[ne] and pow[i] then dfs(ne,y) end; end; procedure dfs2(k:longint); begin while root[k]<>lca1 do begin if not pow[roof[k]] then begin pow[roof[k]]:=true;dec(ans) end; k:=root[k] end; if (k<>1)and (not pow[roof[k]]) then begin pow[roof[k]]:=true;dec(ans) end; b[k]:=lca1 end; procedure origin; var i:longint; begin fillchar(tail,sizeof(tail),0);fillchar(next,sizeof(next),0); fillchar(sora,sizeof(sora),0);fillchar(ob,sizeof(ob),0); ss:=n; for i:=1 to n do tail[i]:=i end; procedure link(x,y:longint); begin inc(ss);next[tail[x]]:=ss;tail[x]:=ss;sora[ss]:=y; inc(ss);next[tail[y]]:=ss;tail[y]:=ss;sora[ss]:=x; ob[ss-1]:=ss;ob[ss]:=ss-1 end; procedure init; var i,k,x,y,x1,y1,l,r,q:longint; begin origin; for i:=1 to m do begin readln(x,y);link(x,y) end; fillchar(pow,sizeof(pow),true);fillchar(pos,sizeof(pos),0); fillchar(root,sizeof(root),0);fillchar(roof,sizeof(roof),0); fillchar(f,sizeof(f),0); fillchar(rel,sizeof(rel),127);fillchar(low,sizeof(low),0); time:=0;len:=0;ans:=0;roof[1]:=1; tarjan(1,1); lcr; fillchar(b,sizeof(b),0);fillchar(v,sizeof(v),true); for i:=1 to n do b[i]:=i; for i:=1 to n do if v[i] then dfs(i,i); readln(q); for i:=1 to q do begin readln(x,y); x1:=find(x);y1:=find(y); if x1<>y1 then begin if pos[x]<pos[y] then begin l:=pos[x];r:=pos[y] end else begin l:=pos[y];r:=pos[x] end; k:=trunc(ln(r-l+1)/ln(2)); lca:=minl(f[l,k],f[r-d[k]+1,k]); lca1:=find(lca); if x1<>lca1 then dfs2(x1); if y1<>lca1 then dfs2(y1) end; writeln(ans) end end; begin //assign(input,'3694.in');reset(input); i:=0; repeat readln(n,m); if (n=0) and (m=0) then break; inc(i);writeln('Case ',i,':'); init; writeln until (n=0) and (m=0); //close(input) end.