• 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:
    ",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
    ",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 }
  • 相关阅读:
    java IO流详解
    java设计模式之单例模式(几种写法及比较)
    JS定时刷新页面及跳转页面
    遍历map的四种方法
    String 中去掉空格
    TSP问题_遗传算法(STL大量使用)
    无向图的深度优先生成树和广度优先生成树
    Prim算法求最小生成树
    哈夫曼编码_静态库
    中序线索化二叉树
  • 原文地址:https://www.cnblogs.com/mjtcn/p/9645678.html
Copyright © 2020-2023  润新知