# 割点，桥模板

low[v]>=pre[u],u为割点。

low[v]>pre[u],(u,v)为割边

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 1000
int pre[maxn], iscut[maxn], low[maxn], dfs_clock;
//iscut[i]判断某个点是否为割点
vector<int>G[maxn];
int dfs(int u, int fa)
{
int lowu=pre[u]=++dfs_clock;
int child=0;
for(int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if(!pre[v])
{
child++;
int lowv=dfs(v,u);
lowu=min(lowu,lowv);
if(lowv>=pre[u])iscut[u]=true;//u不为树根，必须满足lowv>=preu
//if(lowv>pre[u])则(u, v)满足桥
}else if(pre[v]<pre[u]&&v!=fa)
lowu=min(lowu, pre[v]);
}
if(fa<0&&child==1)iscut[u]=0;//如果u为树根，那么u必须有多于1棵子树，才能为割点
low[u]=lowu;
return lowu;
}
void find_cutpoint(int n)
{
memset(pre,0,sizeof(pre));
memset(low,0,sizeof(low));
memset(iscut,0,sizeof(iscut));
dfs_clock=0;
for(int i = 1; i <= n; i++)if(!pre[i])dfs(i,-1);//点从1开始
}
int main()
{
int n;
while(~scanf("%d",&n)&&n)
{
for(int i = 1; i <= n; i++)G[i].clear();
int tmp;
while(scanf("%d",&tmp)&&tmp)
{
while(getchar()!='\n')
{
int t;
scanf("%d",&t);
G[tmp].push_back(t);
G[t].push_back(tmp);
}
}
find_cutpoint(n);
int ans = 0;
for(int i = 1; i <= n; i++)if(iscut[i])ans++;
printf("%d\n",ans);
}
return 0;
}


09-03 104
10-27 330

10-11 364
11-07 133
08-04 22
10-05 394
07-03 83