#4376 种树
题面
有一个N个点M条边的无向图,每次选择一个点,并删除该点及其相邻的边,如果变成了一棵树,这这个点就是我们需要的。请找出满足条件的可能的点。
对于 40%的数据:
n
,
m
<
=
1000
n,m<=1000
n,m<=1000;
另外存在 10%的数据:
m
=
n
−
1
m=n-1
m=n−1;
另外存在 20%的数据:
m
=
n
m=n
m=n;
对于 100%的数据:
n
,
m
<
=
100000
n,m<=100000
n,m<=100000
输入
第一行2个正整数N,M,表示N个点M条边,保证N>=2
接下来M行,每行2个整数U,V表示u,v有一条无向边,数据保证无自环与重边
输出
第一行是一个整数ans,表示一共有ans个可能的点
接下来一行输出ans个整数,用空格隔开,按从小到大输出可能的点(数据保证至少存在一个可能的点)
样例输入
6 6
1 2
1 3
2 4
2 5
4 6
5 6
样例输出
3
4 5 6
SOL
暴力怎么打不说了,讲一讲正解。
观察发现,你删去一个点后,剩下
n
−
1
n-1
n−1个点,得到一棵树,意味着剩下
n
−
2
n-2
n−2条边,也就是说你删掉一个度数为
m
−
(
n
−
2
)
m-(n-2)
m−(n−2)的点,如果图仍然连通(也就是说你删掉的那个点在原图中不是一个割点),那么就把这个点统计到答案中。
记录每个点的度数,并用
t
a
r
j
a
n
tarjan
tarjan求出原图中的割点,这道题就做完了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define N 100005
int n,m;
int ans,output[N];
vector<int>e[N];
inline int rd(){
int data=0,w=1;static char ch=0;ch=getchar();
while((!isdigit(ch))&&ch!='-')ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
return data*w;
}
int dfn[N],low[N],tcnt,cut[N];
void dfs(int u,int fa){
int son=0;
low[u]=dfn[u]=++tcnt;
for(int register i=0;i<e[u].size();i++){
int v=e[u][i];
if(!dfn[v]){
son++;
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])cut[u]=1;
}
else if(dfn[v]<dfn[u]&&v!=fa)low[u]=min(low[u],dfn[v]);
if(son==1&&u==1)cut[u]=0;
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n=rd();m=rd();
for(int register i=1;i<=m;i++){
int u=rd(),v=rd();
e[u].push_back(v);
e[v].push_back(u);
}
for(int register i=1;i<=n;i++)if(!dfn[i])dfs(i,i);
for(int register i=1;i<=n;i++){
if(e[i].size()==m-n+2&&!cut[i])output[++ans]=i;
}
printf("%d\n",ans);
for(int register i=1;i<=ans;i++)printf("%d ",output[i]);
return 0;
}