冗余路径 Redundant Paths
sol:
如果两点间存在至少两条不重复的路径,这说明他们两点在同一个边双连通分量(不存在割边)。
那么可以进行e-DCC的缩点,得到一棵树。
对于这棵树广泛意义上的叶子节点(度数为1)而言,都还至少需要一条边连向他。
那么可以贪心的一次连两个叶子节点,答案显然就是(cnt+1>>1)。
#include<bits/stdc++.h>
#define IL inline
#define RG register
#define DB double
#define LL long long
using namespace std;
const int N=5005;
const int M=1e4+5;
int n,m,tot,cnt,leaf,Time,head[N],bel[N],vis[N],low[N],dfn[N],bri[M<<1];
struct edge{int x,y;}s[M];
struct EDGE{int next,to;}e[M<<1];
IL int gi() {
RG int x=0,p=1; RG char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') p=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
return x*p;
}
IL void New() {
tot=0;
memset(&e,0,sizeof(e));
memset(head,0,sizeof(head));
}
IL void make(int a,int b) {
e[++tot]=(EDGE){head[a],b},head[a]=tot;
e[++tot]=(EDGE){head[b],a},head[b]=tot;
}
void Tarjan(int x,int fx) {
RG int i,y;
dfn[x]=low[x]=++Time;
for(i=head[x];i;i=e[i].next) {
if(!dfn[y=e[i].to]) {
Tarjan(y,x),low[x]=min(low[x],low[y]);
if(low[y]>dfn[x]) bri[i]=bri[i^1]=1;
}
else if(y!=fx) low[x]=min(low[x],dfn[y]);
}
}
void dfs(int x) {
RG int i,y;
bel[x]=cnt,vis[x]=1;
for(i=head[x];i;i=e[i].next)
if(!vis[y=e[i].to]&&!bri[i]) dfs(y);
}
void dfs2(int x,int fx) {
RG int i,y,fl=0;
for(i=head[x];i;i=e[i].next)
if((y=e[i].to)!=fx) fl=1,dfs2(y,x);
if(!fl) ++leaf;
}
int main()
{
RG int i,b=0;
n=gi(),m=gi(),tot=1;
for(i=1;i<=m;++i)
s[i].x=gi(),s[i].y=gi(),make(s[i].x,s[i].y);
for(i=1,Tarjan(1,0);i<=n;++i)
if(!vis[i]) ++cnt,dfs(i);
if(cnt==1) return puts("0"),0;
for(i=1,New();i<=m;++i)
if(bel[s[i].x]!=bel[s[i].y]) make(bel[s[i].x],bel[s[i].y]);
dfs2(1,0);
for(i=head[1];i;i=e[i].next) ++b;
if(b==1) ++leaf;
printf("%d
",leaf+1>>1);
return 0;
}