



题目链接:树上摩托
分析
一开始想偏了,没有看到约数的性质。后面玩玩发现了一些性质:
- 树的大小只可能是 N 的约数
- 树的大小确定的话,方案最多只有一种
将原树看做有根树,预处理每个节点的父亲,每个节点的size标为1。
然后循环从后往前取出队列里面的数(只能手动队列qwq)。累加子树size大小。
开一个桶,统计每个大小的子树的数量。
枚举n的所有约数(树的大小)k,判断子树大小是k的倍数的节点数量是否为n/k。
上代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
int n,tot,hd[1000001],size[1000001],fa[1000001],num[1000001],q[1000001];
struct node
{
int to,next;
}e[2000001];
void add(int x,int y)
{
e[++tot]=(node){y,hd[x]};
hd[x]=tot;
}
void bfs()
{
int h=0,t=1;
q[1]=1;
while(h<t)
{
h++;
int x=q[h];
size[x]=1;
for(int i=hd[x];i>0;i=e[i].next)
{
if(e[i].to!=fa[x])
{
fa[e[i].to]=x;//标记爸爸
q[++t]=e[i].to;
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
bfs();
for(int i=n;i>=1;i--)
{
int x=q[i];//只能手动队列,STL只能取第一个
for(int j=hd[x];j>0;j=e[j].next)
{
if(e[j].to!=fa[x])
{
size[x]+=size[e[j].to];
}
}
}
for(int i=1;i<=n;i++)
{
num[size[i]]++;
}
long long ans=0;
for(int i=1;i<=n;i++)
{
if(n%i==0)//枚举约数
{
int s=0;
for(int k=1;k<=n/i;k++)//枚举约数的倍数
{
s+=num[k*i]; //累加有多少个大小为k的倍数的子树
}
if(s==n/i) ans++; //如果刚好能分成一样的
}
}
cout<<ans;
return 0;
}
博客探讨了一道关于树上摩托的问题,分析了树的大小与N的约数关系,指出树的大小只能是N的约数且大小确定的方案唯一。通过BFS预处理节点信息,然后枚举并统计子树大小,判断能否均分成相同大小的子树。代码实现了该算法。

被折叠的 条评论
为什么被折叠?



