以后这种模板还是看书比较好,网上的博客都讲得不是很好,推荐lyd写的《算法竞赛进阶指南》写的,代码比较现代化(是的我就是在嫌弃紫书白书),然后讲解的也通俗易懂。
在各类tarjan算法中,dfn[i]表示到达i的序号,low[i]表示i所能间接到达的点中dfn最小的值。
那么如果一个点u是割点,如果他是根节点,那么就要有两个儿子节点的dfn[u]<=low[v],如果不是,那么只要有一个儿子节点满足上个条件就是割点。
理性理解一下,就是在dfs的情况下,某一个点u,他的儿子v无法到达在u前面访问到的点,那么把u这个点去掉,v就与u上边的点不连通了
如果是根节点,那么u是第一个访问到的点,则需要两个满足这个条件的v1,v2,如果u断了,那么v1,v2就不连通了
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxl 110
using namespace std;
int n,cnt,ans,root,ind;
int ehead[maxl],dfn[maxl],low[maxl];
bool cut[maxl],in[maxl];
struct ed
{
int to,nxt;
}e[maxl*maxl*2];
inline void prework()
{
for(int i=1;i<=n;i++)
{
ehead[i]=0,dfn[i]=0,low[i]=0;
in[i]=false;cut[i]=false;
}
cnt=0;ans=0;root=0;ind=0;
char ch;int v,u;
while(scanf("%d",&u) && u)
{
do
{
v=0;
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') v=v*10+ch-'0',ch=getchar();
e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
e[++cnt].to=u;e[cnt].nxt=ehead[v];ehead[v]=cnt;
if(ch==' ') continue;
}while(ch!='\n');
}
}
inline void tarjan(int u,int fa)
{
int v;
dfn[u]=++ind;low[u]=ind;
int son=0;
for(int i=ehead[u];i;i=e[i].nxt)
{
v=e[i].to;
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[v],low[u]);
if(low[v]>=dfn[u])
{
son++;
if(u!=root || son>1)
cut[u]=true;
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
inline void mainwork()
{
for(int i=1;i<=n;i++)
if(!dfn[i])
{
root=i;
tarjan(i,i);
}
}
inline void print()
{
for(int i=1;i<=n;i++)
if(cut[i])
ans++;
printf("%d\n",ans);
}
int main()
{
// freopen("F.in","r",stdin);
while(~scanf("%d",&n) && n)
{
prework();
mainwork();
print();
}
return 0;
}