[bzoj3004] [SDOi2012]吊灯
Description
Alice家里有一盏很大的吊灯。所谓吊灯,就是由很多个灯泡组成。只有一个灯泡是挂在天花板上的,剩下的灯泡都是挂在其他的灯泡上的。也就是说,整个吊灯实际上类似于[b]一棵树[/b]。其中编号为 1 的灯泡是挂在天花板上的,剩下的灯泡都是挂在编号小于自己的灯泡上的。
现在,Alice想要办一场派对,她想改造一下这盏吊灯,将灯泡换成不同的颜色。她希望相同颜色的灯泡都是相连的,并且每一种颜色的灯泡个数都是相同的。
Alice希望你能告诉她,总共有哪些方案呢?
Alice是一个贪心的孩子,如果她发现方案不够多,或者太多了,就会很不高兴,于是她会尝试调整。对于编号为[b]x(x≠1)[/b]的灯泡,如果原来是挂在编号为[b]f[x][/b]的灯泡上,那么Alice会把第x个灯泡挂到[b]第 ( f[x] + 19940105 ) mod (x-1) + 1 个[/b]灯泡上。
由于九在古汉语中表示极大的数,于是,[b][color=#FF0000]Alice决定只调整9次[/color][/b]。对于原始状态和每一次调整过的状态,Alice希望你依次告诉她每种状态下有哪些方案。
Input
第一行一个整数n,表示灯泡的数量。
接下来一行,有n-1个整数Ui,第i个数字表示第i+1个灯泡挂在了Ui个的下面。保证编号为1的灯泡是挂在天花板上的。数字之间用逗号(西文标点)" , "隔开且最后一个数字后面没有逗号。
Output
对于10种状态下的方案,需要按照顺序依次输出。
对于每一种状态,需要先输出单独的一行,表示状态编号,如样例所示。
之后若干行,每行1个整数,表示划分方案中每种颜色的灯泡个数。按升序输出。
HINT
对于100%的数据,n<=1.2*10^6。
Solution
奇怪的结论题。。
题目其实就是问一棵树能否被分割成给定大小的大小相同的若干个联通块。
然后对于一颗合法的树,假设联通块的大小为\(x\),那么可以分成\(n/x\)个块,显然要满足$ n \mid x$。
考虑每一个联通块,显然最上面那个点的\(sz\)一定是\(x\)的倍数,
那么一共就有\(n/x\)个点的\(sz\)为\(x\)的倍数。
反过来想如果一棵树有\(n/x\)个点的\(sz\)为\(x\)的倍数,那么显然这棵树可以被分成\(n/x\)个大小为\(x\)的联通块。
由题目\(fa\)数组的构造可知,\(fa[x]<x\),所以并不需要\(dfs\),逆序循环一遍就好了。
代码非常简洁。
#include<bits/stdc++.h>
using namespace std;
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int maxn =1.2e6+1;
int f[maxn],sz[maxn],cnt[maxn];
int main() {
int n;read(n);
for(int T=1;T<=10;T++) {
for(int i=2;i<=n;i++)
if(T==1) read(f[i]);else f[i]=(f[i]+19940105)%(i-1)+1;
for(int i=1;i<=n;i++) sz[i]=1,cnt[i]=0;
for(int i=n;i;i--) sz[f[i]]+=sz[i],cnt[sz[i]]++;
printf("Case #%d:\n",T);
for(int i=1;i<=n;i++) {
if(n%i) continue;int tmp=0;
for(int j=i;j<=n;j+=i) tmp+=cnt[j];
if(tmp==(n/i)) write(i);
}
}
return 0;
}