P2351 [SDOi2012]吊灯

P2351 [SDOi2012]吊灯

https://www.luogu.org/problemnew/show/P2351
 
 

题意:
  一棵树,能否全部分成大小为x的联通块。

分析:
  显然x是n的约数。然后对于一个约数x,判断能否分成 $ \frac{n}{x} $ 个大小为x的联通块。

  结论:如果x可以,那么一定存在$ \frac{n}{x} $个节点的子树大小是x的倍数。

  证明:上面的结论说明的也就是每个大小是x的倍数的点,对答案的贡献是1(每个点都可以分出一个大小为x的块),加起来就是$ \frac{n}{x} $。

  现在就要考虑一个点u的siz是kx,然后它的子树里如果没有其他点的siz的是x的倍数的话,它的贡献是1,它可以从根节点开始,分出一个包含根节点,一共x个点的联通块。

  然后考虑u的子树里还有一个点v的siz是x的倍数,那么如果它们还能分成两个大小为x的块的话,那么每个这样的点的贡献还是1。首先从在v的子树里一定可以从根开始分出一个大小为x的块(u在其中),然后u的子树里需要找一个大小为x的块,且不使用v中的点。假设去掉v中的点还剩siz[u]-siz[v]个,这也是x的倍数,所以u的子树里,从根开始,不占用v的点,还可以分出一个大小为x的块。说明u的子树可以贡献2,uv各自贡献1。

  如果u的子树还有这样的点,那么把v删掉,还是两个点的情况,所以还是合法的。

  到此发现每个大小为x的倍数的点,会对答案贡献1。$ \frac{n}{x} $$个,就会有$ \frac{n}{x} $个大小为x的联通块,如果小于则不行。

 
代码:
 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<iostream>
 6 #include<cctype>
 7 #include<set>
 8 #include<vector>
 9 #include<queue>
10 #include<map>
11 using namespace std;
12 typedef long long LL;
13 
14 inline int read() {
15     int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
16     for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
17 }
18 
19 const int N = 12000005;
20 
21 int fa[N], cnt[N], siz[N], n;
22 vector<int> v;
23 
24 bool check(int x) {
25     int res = 0;
26     for (int i=x; i<=n; i+=x) res += cnt[i];
27     return res >= n / x;
28 }
29 
30 int main() {
31     n = read();
32     for (int lim=sqrt(n),i=1; i<=lim; ++i) {
33         if (n % i == 0) {
34             v.push_back(i);
35             if (n / i != i) v.push_back(n / i);
36         }
37     }
38     sort(v.begin(), v.end());
39     for (int i=2; i<=n; ++i) 
40         fa[i] = read();
41     for (int T=1; T<=10; ++T) {
42         printf("Case #%d:\n",T);
43         for (int i=1; i<=n; ++i) cnt[i] = 0, siz[i] = 1;
44         for (int i=n; i>=1; --i) siz[fa[i]] += siz[i], cnt[siz[i]] ++;
45         for (int i=0; i<v.size(); ++i) 
46             if (check(v[i])) printf("%d\n",v[i]);
47         if (T != 10) for (int i=2; i<=n; ++i) 
48             fa[i] = (fa[i] + 19940105) % (i - 1) + 1;
49     }
50     return 0;
51 }

 

posted @ 2018-09-14 11:50  MJT12044  阅读(241)  评论(0编辑  收藏  举报