洛谷P6591题解

方法

一个一个点建树必定会超时,所以说我们就选择以一号点为根建树,此时便可以得到一个完整的树,而对于以叶子节点为根(入度为 \(1\)),则必定成立,对于中间任何一个非叶子且非根的节点为根,必然有一个父节点和多个子结点,子节点所形成的子树相较于以 \(1\) 为根的树的子树不变,而此时这个新的根的父节点变成了它的子节点,而此时,我们可以发现,这个新产生的子树是原来的总结点个数减去以目前的根节点为根的子树。也就是说,对于一个有父亲也有儿子的节点,从 \(1\) 为根的数统计出每个点的子树,存放在名为 \(cnt\) 的数组里,此时若以 \(i\) 号节点为根,那么 \(cnt_1-cnt_i\) 便需要与i的子节点 \(i_p\)\(1\leq p \leq i\) 的总子节点数)所呈现的 \({cnt_{i_p}}\) 相等。

所以说需要 dfs 和判断即可。

代码实现

#include<stdio.h>
#include<vector>
#include<queue>
#include<string.h>
using namespace std;
vector<int>map[1000005];
priority_queue<int,vector<int>,greater<int> >ans;//自动排序优先队列
int n;
int numf[1000005];
bool visit[1000005];
int dfs(int num){
	visit[num]=1;
	for(int i=0;i<map[num].size();i++){
		if(visit[map[num][i]]!=1){
			numf[num]+=dfs(map[num][i]);
		}
	}
	return numf[num];
}//将以1为根的树中所有的子树搜索出来
int main(){
	scanf("%d",&n);
	for(int i=1;i<=1000004;i++){
		numf[i]=1;//初始化
	}
	for(int i=1;i<=n-1;i++){
		int x,y;
		scanf("%d%d",&x,&y);//本题无需快读,scanf即可
		map[x].push_back(y);
		map[y].push_back(x);//无向图双向存边
	}
	dfs(1);
	for(int i=1;i<=n;i++){
		bool flag=0;
		if(map[i].size()==1){
			ans.push(i);
			continue;
		}//若该节点为叶子节点,则入队
		if(i==1){
			for(int j=0;j<map[i].size();j++){
				if(j!=0&&numf[map[i][j]]!=numf[map[i][j-1]]){
					flag=1;
					break;
				}
			}
		}//若该节点为根节点		
		else for(int j=0;j<map[i].size();j++){
			if(numf[1]-numf[i]!=numf[map[i][j]]&&numf[map[i][j]]<numf[i]){
				flag=1;
				break;
			}
		}//若该节点非叶子节点也非根节点
		if(flag==0){
			ans.push(i);
		}
	}
	int k=ans.size();
	for(int i=0;i<k;i++){
		printf("%d ",ans.top());
		ans.pop();//答案输出,不要忘记pop
	}
	return 0;
}
posted @ 2022-11-09 23:26  nxhx  阅读(25)  评论(0)    收藏  举报
1 2 3
4