P4748 [CERC2017] Justified Jungle 题解

题目链接

我的博客

思路

这道题打眼一看一定是一个数学题。所以我们尝试使用数学知识来解决。

如果你需要删除 \(k\) 条边,那么这时候你会把树分成 \(k+1\) 个联通块。因为要包含相同数量的节点,所以每一个联通块的大小应该均为 \(\frac{n}{k+1}\)。所以我们得出一个结论:\(k+1\) 一定是 \(n\) 的约数。

于是我们枚举 \(k\),判断每一个 \(k\) 合法或者不合法即可。

假设我们删除掉 \(fa_u\)\(u\) 之间的这条边,那么子树 \(u\) 的大小一定等于 \(\frac{n}{k+1}\)

于是判断的思路就有了:我们找每一个子树大小是 \(\frac{n}{k+1}\) 的倍数的节点,如果有 \(k+1\) 个那么就合法,否则不合法。

时间复杂度大概不劣于 \(O(n \log n)\)。笔者不太会算这种,欢迎纠正!

代码

const int N=1e6+10;
int n,siz[N],sum[N];
struct edge{
	int nxt,to;
}e[N<<1];
int head[N],num_Edge=0;
void add_Edge(int from,int to){
	e[++num_Edge].nxt=head[from];
	e[num_Edge].to=to;
	head[from]=num_Edge;
}
void dfs(int u,int fa){
	siz[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		siz[u]+=siz[v];
	}
	sum[siz[u]]++;//统计子树大小
}
bool check(int k){//判断 k 是否合法
	if(n%(k+1)) return 0;//不是倍数直接不合法!笔者写代码的时候忘了,T飞了
	int x=n/(k+1),y=0;
	int t=x;
	while(t<=n){
		y+=sum[t];
		t+=x;
	}
	if(y==k+1) return 1;
	return 0;
}
signed main(){
	n=Read();
	for(int i=1;i<n;i++){
		int u=Read(),v=Read();
		add_Edge(u,v);
		add_Edge(v,u);
	}
	dfs(1,0);//以 1 为根节点
	for(int i=1;i<n;i++){
		if(check(i)) {
			printf("%d ",i);
		}
	}
	return 0; 
}
posted on 2025-11-11 15:52  _Liuliuliuliuliu  阅读(1)  评论(0)    收藏  举报