题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。
分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。
缩点后,新图是一棵树,树的边就是原无向图的桥。
现在问题转化为:在树中至少添加多少条边能使图变为双连通图。
结论:添加边数=(树中度为1的节点数+1)/2
具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。
求边双连通分量的方法:
Low[u]=Min{
DFN(u),
DFN(v), 存在无向边(u,v) 但(u,v)不能是搜索树中的边
Low(v) v是u扩展出来的儿子
}
上面的DFN(x)表示x这个结点是第几个入栈的。
dfs完后若low[x]=low[y]则x和y在同一双连通分量内。
一开始跟标程对过无数遍了还是不知道为什么WA,后来在网上看到说可能会有重边,也就是在算节点的度的时候会多算,所以把重边去掉就可以A了。
代码:
var n,m,t,sum,e:longint; d,low,dfn,last:array[1..5000] of longint; v:array[1..5000,1..5000] of boolean; side:array[1..20000] of record x,y,next:longint; end; procedure add(x,y:longint); begin inc(e); side[e].x:=x; side[e].y:=y; side[e].next:=last[x]; last[x]:=e; inc(e); side[e].x:=y; side[e].y:=x; side[e].next:=last[y]; last[y]:=e; end; procedure init; var i,x,y:longint; begin readln(n,m); fillchar(v,sizeof(v),true); for i:=1 to m do begin readln(x,y); if not v[x,y] then continue; v[x,y]:=false; v[y,x]:=false; add(x,y); end; end; function min(x,y:longint):longint; begin if x<y then exit(x) else exit(y); end; procedure dfs(x,fa:longint); var i:longint; begin inc(t); dfn[x]:=t; low[x]:=t; i:=last[x]; while i>0 do with side[i] do begin if dfn[y]=0 then begin dfs(y,x); low[x]:=min(low[x],low[y]); end else if y<>fa then low[x]:=min(low[x],dfn[y]); i:=next; end; end; procedure main; var i,ans,j:longint; begin dfs(1,1); for i:=1 to e do with side[i] do if low[x]<>low[y] then inc(d[low[x]]); ans:=0; for i:=1 to n do if d[i]=1 then inc(ans); writeln((ans+1) div 2); end; begin init; main; end.