poj 3177 Redundant Paths 边双连通分量

来源:互联网 发布:天天向上 知乎 编辑:程序博客网 时间:2024/06/06 06:30

题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。


分析:在同一个边双连通分量中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。

缩点后,新图是一棵树,树的边就是原无向图的桥。

现在问题转化为:在树中至少添加多少条边能使图变为双连通图。

结论:添加边数=(树中度为1的节点数+1)/2

具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

求边双连通分量的方法:

Low[u]=Min{

DFN(u),

DFN(v), 存在无向边(u,v) (u,v)不能是搜索树中的边

Low(v)  vu扩展出来的儿子

}

上面的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.


1 0