本题详解在前面一篇文章
Tarjan缩点的含义:
low值相等的点在一个双连通分量中,把一个分量看成一个点,重构成一棵树
定理:对于一棵叶子节点数为n的树,需要加(n+1)/2 条边把它变成双连通的。因此本题缩完点之后求一下这个值就是答案。
对于有重边的边双连通分量,如果从u点有两条边到它的father,那么dfn[fa]也可以用来更新lowu。下面这个模板可以处理有重边的情况。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define maxn 1005 using namespace std; #define maxe 3005 #define CLR(a,b) memset(a,b,sizeof(a)) int dfn[maxn],iclock,pa[maxn],col[maxn],deg[maxn],bu[maxn],bv[maxn],bn,cnt; int N,R; struct Edge{ int u,v; Edge * next; }E[maxe],*tot,*head[maxn]; int Find(int x) { return pa[x]<0?x:pa[x]=Find(pa[x]); } void Union(int x,int y){ x=Find(x),y=Find(y); if(x==y) return ; if(pa[x]<pa[y]){pa[x]+=pa[y]; pa[y]=x;} else {pa[y]+=pa[x];pa[x]=y;} } int Tarjan(int u,int fa=-1){ int lowu=dfn[u]=++iclock,tofa=0; for(Edge *p=head[u];p;p=p->next){ int v=p->v; if(!dfn[v]){ int lowv=Tarjan(v,u); lowu=min(lowu,lowv); if(lowv>dfn[u]) bu[++bn]=u,bv[bn]=v; else Union(u,v); } else if(dfn[v]<dfn[u]&&(v!=fa||tofa)) lowu=min(lowu,dfn[v]); if(fa==v) tofa=1; } return lowu; } void init(){ tot=E;CLR(head,0);CLR(dfn,0);CLR(col,0);CLR(pa,-1); cnt=bn=iclock=0; } void Add(int u,int v){ tot->u=u;tot->v=v;tot->next=head[u];head[u]=tot++; } void build(){ tot=E; CLR(head,0); for(int i=1;i<=bn;i++){ int u=col[bu[i]],v=col[bv[i]]; Add(u,v);Add(v,u); } } int find_bcc(){ Tarjan(1); for(int i=1;i<=N;i++){ int k=Find(i); if(!col[k]) col[k]=++cnt; col[i]=col[k]; } build(); for(int i=1;i<=bn;++i) { ++deg[col[bu[i]]];++deg[col[bv[i]]]; } int ret=0; for(int i=1;i<=cnt;i++) if(deg[i]==1) ret++; return (ret+1)/2; } int main(){ scanf("%d%d",&N,&R); init(); for(int i=0;i<R;i++){ int u,v; scanf("%d%d",&u,&v); Add(u,v); Add(v,u); } int res=find_bcc(); printf("%d\n",res); return 0; }