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;
}
浙公网安备 33010602011771号