[8.21NOIP模拟赛]决战【tarjan】

正题


题目大意

n n n个点 m m m条边的联通图,去掉一个点使得其变为一棵树

求能去掉哪些点


解题思路

首先去掉后就是一棵 n − 1 n-1 n1个节点的树,也就是有 n − 2 n-2 n2条边,这样我们就需要去掉 m − n + 2 m-n+2 mn+2条边,也就是去掉入度为 m − n + 2 m-n+2 mn+2的点。

但是需要去掉后图任然是联通图,也就是去掉的是非割点, t a r j a n tarjan tarjan求割点即可。


c o d e code code

#include
#include
#include
using namespace std;
const int N=1e5+10;
struct node{
	int to,next;
}a[N*2];
int n,m,tot,cnt,in[N],ls[N],dfn[N],low[N];
bool mark[N];
void addl(int x,int y){
	a[++tot].to=y;
	a[tot].next=ls[x];
	ls[x]=tot;
}
void tarjan(int x){
	dfn[x]=low[x]=++cnt;
	int flag=0;
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if(dfn[x]<=low[y]){
				flag++;
				if((x!=1||flag>1)&&!mark[x])
					mark[x]=1;
			}
		}
		else low[x]=min(low[x],dfn[y]);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		addl(x,y);addl(y,x);
		in[x]++;in[y]++;
	}
	tarjan(1);int ans=0;
	for(int i=1;i<=n;i++)
		if(!mark[i]&&in[i]==m-n+2)
			ans++;
	printf("%d\n",ans);
	for(int i=1;i<=n;i++)
		if(!mark[i]&&in[i]==m-n+2)
			printf("%d ",i);
}

你可能感兴趣的:(图论,tarjan)