题解 P4748 【[CERC2017]Justified Jungle】
不错的思维题 7.5/10
假设你要切k条边,那么你会把这棵树切成k+1个连通块,每一块的大小为n/(k+1),则k+1必定是n的约数
枚举约数?T了
任意选一个点为根,建树。假设你切掉了连接(fa[u],u)的一条边,那么u这棵子树大小必定是n/(k+1)的倍数。那我们找出满足子树大小等于n/(k+1)的倍数的点,若数量等于(k+1)那么一定可行。
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN=2010000;
int n,siz[MAXN],sum[MAXN],q[MAXN],ans;
int tot,front[MAXN],to[MAXN],nxt[MAXN];
void init(int u,int v)
{
to[++tot]=v;
nxt[tot]=front[u];
front[u]=tot;
}
void dfs(int u,int father)
{
siz[u]++;
for(int i=front[u];i;i=nxt[i])
{
int v=to[i];
if(v==father)continue;
dfs(v,u);
siz[u]+=siz[v];
}
sum[siz[u]]++;
}
bool check(int x)
{
x++;
if(n%x)return 0;
int u=n/x,v=0;
for(int i=u;i<=n;i+=u)v+=sum[i];
return (v==x);
}
int main()
{
// freopen("test.in","r",stdin);
//freopen("test.out","w",stdout);
scanf("%d",&n);
for(int i=2,u,v;i<=n;i++)
{
scanf("%d%d",&u,&v);
init(u,v);
init(v,u);
}
dfs(1,0);
for(int i=1;i<n;i++)
if(check(i))
q[++ans]=i;
//printf("%d\n",ans);
for(int i=1;i<=ans;i++)printf("%d ",q[i]);
return 0;
}