• SP1480 PT07D


    给出两个数 (n,p),你需要求出下面四个问题的答案:

    1. (n) 个点的带标号无根树数量。
    2. (n) 个点的带标号有根树数量。
    3. (n) 个点的无标号有根树数量。
    4. (n) 个点的无标号无根树数量。

    答案对 (p) 取模。

    树计数四合一

    对于前两问可以直接用Cayley来做。

    Cayley说的就是(n)个点的带标号无根树的方案数是(n^{n-2})

    那么有根树的方案数是(n^{n-1})

    考虑证明,先从有根树入手,假设当前有(k)个子树,那么我们可以任选一棵子树上的一个点连边到另一棵子树,那么方案数就是(n(k-1))

    假设刚开始的(n)个点是(n)个子树,而每次操作会使子树减少一个,那么方案数为(prod_{i=1}^{n-1}ni=n^{n-1}(n-1)!)

    而实际上我们连边的顺序是不需要考虑的,需要除上((n-1)!),所以最终方案数为(n^{n-1})

    然后考虑后两问,变得比较困难。

    我们注意(EGF)Exp的组合意义,(exp(F(x))=sum_{ige1} frac{F^i(x)}{i!}),就是将(n)个有标号数放进若干个非空集合的方案数。

    那么变换到无标号,那么有(Euler)变换如下:

    [mathcal{E}(F(x))=prod_{ige1}(frac{1}{1-x^i})^{f_i} ]

    然后我们回去第三问,如果你选择一个点当根,那么剩下的子树之间没有关联,就类似于刚才那个无标号计数。

    那么设(f_n)表示(n)个点时无标号有根树的方案数,(F(x))(f)对应的生成函数,那么有:

    [F(x)=xmathcal{E}(F(x))=xprod_{ige1}(frac{1}{1-x^i})^{f_i} ]

    (prod(frac{1}{1-x^i})^{f_i})提出来做ln可以得到:

    [ln(prod_{ige1}(frac{1}{1-x^i})^{f_i})=sum_{ige1}f_isum_{jge1}frac{1}{j}x^{ij}=sum_{jge1}frac{1}{j}sum_{ige1}f_i(x^j)^i=sum_{ige1}frac{1}{i}F(x^i) ]

    再Exp回去代回原式有:

    [F(x)=xExp(sum_{ige1}frac{1}{i}F(x^i)) ]

    这个式子可以用牛顿迭代在(O(nlogn))完成,但是这个题可以(O(n^2))递推,于是我们继续化简。

    两边求导有:

    [xF'(x)=F(x)+F(x)(sum_{ige1}F'(x^i)x^i) ]

    我们发现((x^i)^k)才有值,那么考虑单独看第(n)项:

    [nf_n=f_n+sum_{j=1}^nf_jsum_{i=1}^nf_ii[i|n-j] ]

    [nf_n=f_n+sum_{j=1}^nf_jsum_{i|n-j}f_ii ]

    那么设(g_n=sum_{i|n}f_ii),那么有:

    [f_n=frac{sum_{j=1}^nf_jg_{n-j}}{n-1} ]

    这样子第三问就完成了。

    然后第四问就完成了一半,我们考虑在有根树的基础上变为无根树,需要把一些重复的树减去,而如果我们统计的有根树的根都是重心,那么一定不会重复,所以可以拿第三问的答案(f_n)减去不是重心的方案数。

    而如果一个根的子树大小超过了(lfloorfrac{n}{2} floor+1),那么这个根肯定不是重心,方案数为(f_if_{n-i}),于是答案为:

    [f_n-sum_{i=lfloorfrac{n}{2} floor+1}^{n-1}f_if_{n-i} ]

    而注意到(n)为偶数时会出现两个重心,所以还要减去(egin{pmatrix}f_{frac{n}{2}}\2end{pmatrix})

    Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    const int N = 1e3;
    using namespace std;
    int p,n,f[N + 5],g[N + 5],ans,inv[N + 5];
    int mypow(int a,int x){int s = 1;for (;x;x & 1 ? s = 1ll * s * a % p : 0,a = 1ll * a * a % p,x >>= 1);return s;}
    void solve1()
    {
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        f[1] = 1;
        g[1] = 1;
        inv[1] = 1;
        for (int i = 2;i <= n;i++)
            inv[i] = 1ll * (p - p / i) * inv[p % i] % p;
        for (int i = 2;i <= n;i++)
        {
            for (int j = 1;j <= i;j++)
                f[i] += 1ll * f[j] * g[i - j] % p,f[i] %= p;
            f[i] = 1ll * f[i] * inv[i - 1] % p;
            for (int j = 1;j <= i;j++)
                if (i % j == 0)
                    g[i] += 1ll * f[j] * j % p,g[i] %= p;
        }
    }
    void solve2()
    {
        solve1();
        int sm = 0;
        for (int i = n / 2 + 1;i <= n - 1;i++)
            sm += 1ll * f[i] * f[n - i] % p,sm %= p;
        ans = (f[n] - sm) % p;
        if (n % 2 == 0)
            ans -= 1ll * f[n / 2] * (f[n / 2] - 1) / 2 % p,ans %= p;
    }
    int main()
    {
        int k;
        while (scanf("%d%d%d",&k,&n,&p) != EOF)
        {
            if (k == 1)
                printf("%d
    ",mypow(n,max(0,n - 2)));
            else
            if (k == 2)
                printf("%d
    ",mypow(n,n - 1));
            else
            if (k == 3)
            {
                solve1();
                printf("%d
    ",(f[n] + p) % p);
            }
            else
            {
                solve2();
                printf("%d
    ",(ans + p) % p);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    SpringBoot-redis-session
    设计模式总结
    linux 查看磁盘信息
    MAC配置JAVA环境变量
    mysql设计规范二
    mysql设计规范一
    Alibaba 镜像
    ELK之Logstash配置文件详解
    Docker 搭建 ELK 读取微服务项目的日志文件
    SpringBoot 读取配置文件的值 赋给静态变量
  • 原文地址:https://www.cnblogs.com/sdlang/p/13805851.html
Copyright © 2020-2023  润新知