zoukankan      html  css  js  c++  java
  • 计树问题小结 version 2.0

    本文接自此篇博客,旨在记录一些更有趣(?)的内容。

    决定完善这篇博客是因为看见了这个题.于是便以这道题目的题解为主线写这篇blog.

    (op=1,2)的时候的答案可以看上文提到的blog.

    无标号有根树计数

    (op=3)时的问题也就是无标号有根树的计数,在上篇blog里以烷烃计数举例,在这里考虑去掉度数限制后的做法。

    (f_i)(i)个点的答案。考虑枚举子树大小以及该大小的子树的数量进行转移,但是复杂度过高。把转移用生成函数的形式写出来的话有:

    [F(x)=xprod_{i>0}(sum_{kgeq 0}x^{ki})^{f_i}=xprod_{i>0}(frac{1}{1-x^i})^{f_i} ]

    其意义如下:最前面乘的(x)表示根节点,(sum_{kgeq 0}x^{ki})表示某个大小为(i)的子树有几个,它的(f_i)次幂则对应着所有大小为(i)的子树的方案数。之后再把(k)用封闭形式丢掉。

    看见这个乘积很烦人,考虑对两边去取(ln).

    [ln F(x)=ln x-sum_{i>0}f_iln(1-x^i) ]

    再同时求个导。

    [frac{F'(x)}{F(x)}=frac{1}{x}+sum_{i>0}if_ifrac{x^{i-1}}{1-x^i} ]

    去分母。

    [xF'(x)=F(x)+F(x)sum_{i>0}if_ifrac{x^i}{1-x^i} ]

    看起来没啥花样了?我们把(f_n)请回来:

    [nf_n=f_n+sum_{i>0}f_isum_{k>0}kf_k([x^{n-i}]frac{x^k}{1-x^k}) ]

    考虑一下后面那个带(x)的项是什么,由于(frac{1}{1-x^k}=sum_{igeq 0}x^{ki}),那么它再乘上(x^k)后,也只有次数是(k)的倍数的项的系数为1,于是我们得到了这样子的式子:

    [nf_n=f_n+sum_{i>0}f_isum_{k>0}kf_k[k|n-i] ]

    稍微整理一下,同时把下标写得更好看些:

    [f_n=frac{sum_{i=1}^{n-1}f_isum_{k|n-i}kf_k}{n-1} ]

    (g_i=sum_{j|i}jf_j),得到了最终的式子:

    [f_n=frac{sum_{i=1}^{n-1}f_ig_{n-i}}{n-1} ]

    边界条件(f_0=0,f_1=1).

    在转移的过程中求出了一个(f)的同时维护(g),总时间复杂度(O(n^2)), 代码见下。

    无标号无根树计数

    思路和烷基计数差不多?

    考虑已经求出了有根树的答案(f_n),如何求无根树的答案(h_n), 主要的问题还是去掉树的形态相同但根不同的方案数。于是想到对每个形态的树钦点某个点为根。这个点最好还要和树的大小有关,不难想到钦点树的重心为根。

    先考虑(n)为奇数的情况,此时每棵树有且仅有一个确定的重心,那么如果当前根不是重心的话,它一定有一个大小大于(lfloor frac{n}{2} floor)的子树,于是有:

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

    接着考虑(n)为偶数的情况,稍微麻烦一点的是我们需要考虑当前树有两个重心的情况。如果只有一个重心的话我们只需要和奇数一样减去不合法的情况。而当有两个重心时,这两个点一定通过一条边相连,且把这条边断开后,只有在形成的两棵子树大小一致但形态不同时才会有多算的情况,综上所述可得计算式:

    [h_n=f_n-sum_{i=frac{n}{2}+1}^{n-1}f_if_{n-i}-dbinom{f_{frac{n}{2}}}{2} ]

    于是就可以做到单个的(O(1))计算了,代码如下:

    int solve3()
    {
    	f[0]=0;f[1]=1;
    	rep(i,1,n) g[i]=1;
    	rep(i,2,n)
    	{
    		f[i]=0;
    		rep(j,1,i) f[i]=add(f[i],mul(f[j],g[i-j]));
    		f[i]=mul(f[i],getinv(i-1));
    		int tmp=mul(f[i],i);
    		for (int j=i;j<=n;j+=i) g[j]=add(g[j],tmp);
    	}
    	return f[n];
    }
    
    int C2(int x) {return mul(mul(x,x-1),inv2);}
    
    int solve4()
    {
    	solve3();h[0]=0;h[1]=1;
    	inv2=getinv(2);
    	rep(i,2,n)
    	{
    		h[i]=0;
    		rep(j,i/2+1,i-1) h[i]=add(h[i],mul(f[j],f[i-j]));
    		if (i%2==0) h[i]=add(h[i],C2(f[i/2]));
    		h[i]=dec(f[i],h[i]);
    	}
    	return h[n];
    }
    

    更高效的解法

  • 相关阅读:
    eclipse lua
    eclipse新建python项Project interpreter not specified
    Laravel Debugbar
    Java中枚举类型简单学习
    SG函数题目
    关于解决博弈论问题的SG函数
    三种典型的博弈论问题
    Java I/O 对象序列化
    Java I/O 文件加锁,压缩
    Java I/O NIO学习
  • 原文地址:https://www.cnblogs.com/encodetalker/p/12695227.html
Copyright © 2011-2022 走看看