[Usaco2006 Jan] Redundant Paths 分离的路径
<body>
<center><h1>1718: [Usaco2006 Jan] Redundant Paths 分离的路径</h1><span class="green">Time Limit: </span>5 Sec <span class="green">Memory Limit: </span>64 MB<br><span class="green">Submit: </span>1132 <span class="green">Solved: </span>590<br>[<a href="submitpage.php?id=1718">Submit</a>][<a href="problemstatus.php?id=1718">Status</a>][<a href="bbs.php?id=1718">Discuss</a>]</center><h2>Description</h2><div class="content">
<div> 为了从F(1≤F≤5000)个草场中的一个走到另一个,贝茜和她的同伴们有时不得不路过一些她们讨厌的可怕的树.奶牛们已经厌倦了被迫走某一条路,所以她们想建一些新路,使每一对草场之间都会至少有两条相互分离的路径,这样她们就有多一些选择.</div>
<div> 每对草场之间已经有至少一条路径.给出所有R(F-1≤R≤10000)条双向路的描述,每条路连接了两个不同的草场,请计算最少的新建道路的数量, 路径由若干道路首尾相连而成.两条路径相互分离,是指两条路径没有一条重合的道路.但是,两条分离的路径上可以有一些相同的草场. 对于同一对草场之间,可能已经有两条不同的道路,你也可以在它们之间再建一条道路,作为另一条不同的道路.</div></div><h2>Input</h2><div class="content">
<div><span style="font-size: medium"> 第1行输入F和R,接下来R行,每行输入两个整数,表示两个草场,它们之间有一条道路.</span></div>
<p></p></div><h2>Output</h2><div class="content">
<div><span style="font-size: medium"> 最少的需要新建的道路数.</span></div></div><h2>Sample Input</h2>
<div class="content"><span class="sampledata">7 7<br>
1 2<br>
2 3<br>
3 4<br>
2 5<br>
4 5<br>
5 6<br>
5 7<br>
<br>
</span></div><h2>Sample Output</h2>
<div class="content"><span class="sampledata">2<br>
</span></div><h2>HINT</h2>
<div class="content"><p></p><p><img height="298" width="795" alt="" src="/JudgeOnline/upload/201401/11(4).jpg"></p><p></p></div><h2>Source</h2>
<div class="content"><p><a href="problemset.php?search=Gold">Gold</a></p></div><center>[<a href="submitpage.php?id=1718">Submit</a>][<a href="problemstatus.php?id=1718">Status</a>][<a href="bbs.php?id=1718">Discuss</a>]</center><br>
<a href="./"><span class="red">HOME</span></a>
<a href="javascript:history.go(-1)"><span class="red">Back</span></a>
</body>
</body>
题解
题意要求的是加最少的边让连通图变成边双。
翻了好久才有一篇讲解了构造的题解。
首先tarjan把边双都缩成一个点。如果个数只有1或2,答案显然。
然后是一棵树的情况,选择一个度数不为1的点(易证存在)做根节点。 如何把这棵树加最少的边变成边双呢?
统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样就可以再缩一次点。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。
为什么是正确的呢?考虑我们的目的,是让这棵树变成一个点。而如果这些叶节点都被上述那种找最长链的方式缩成一个点,那么整棵树也应该被缩成一个点了。还是感性理解啊……
#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-') w=-w;
for(;isdigit(ch);ch=getchar()) data=data*10+ch-'0';
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
using namespace std;
co int N=5e3+1,M=2e4+2;
int n,m,dfn[N],low[N],num;
int head[N],edge[M],next[M],tot=1;
int st[N],top;
int dcc[N],deg[N],cnt,ans;
bool v[N];
il void add(int x,int y){
edge[++tot]=y,next[tot]=head[x],head[x]=tot;
}
void tarjan(int x){
dfn[x]=low[x]=++num;
for(int i=head[x];i;i=next[i]){
if(v[i]) continue;
int y=edge[i];
if(!dfn[y]){
st[++top]=y;
v[i]=v[i^1]=1;
tarjan(y);
v[i]=v[i^1]=0;
low[x]=min(low[x],low[y]);
}
else low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
++cnt;
while(top){
dcc[st[top]]=cnt;
if(st[top--]==x) break;
}
}
}
int main(){
read(n),read(m);
for(int x,y;m--;){
read(x),read(y);
add(x,y),add(y,x);
}
st[top=1]=1,tarjan(1);
for(int i=2;i<=tot;i+=2){
int x=edge[i],y=edge[i^1];
if(dcc[x]==dcc[y]) continue;
++deg[dcc[x]],++deg[dcc[y]];
}
for(int i=1;i<=n;++i) ans+=deg[i]==1;
printf("%d\n",(ans+1)/2);
return 0;
}
静渊以有谋,疏通而知事。