Luogu4500 [ZJOI2018]树

Luogu4500 [ZJOI2018]树

\(dp,\)群论

题意简述:求\(k(k \le 10^9)\)棵大小为\(n(n \le 2000)\)的随机有根树(根为\(1\),生成方式为每个节点\(i(i>1)\)随机认\([1,i)\)中一个节点作为祖先)两两同构的概率。

\(Part 1\)

我们先考虑答案是什么?

先计算总方案数,根据树的生成方式,总共有\((n-1)!\)种不同的树,那么总方案数自然而然就是\([(n-1)!]^k\)

我们将大小为\(n\)的树按照同构的原则划分成一些等价类,对于一种等价类,我们设其大小为\(sz_i\)

最终树的形态属于其中一个等价类。

那么本题的答案就是:

\[\frac{1}{[(n-1)!]^k} \sum_i sz_i^k \]

\(Part 2\)

设计一个\(DP\),令\(dp(i,j)\)表示一个大小为\(i\)的树,其所有等价类大小的\(jk\)次方的和(没看错,\(jk\)就是\(j\)\(k\))。

那么答案就是\(dp(n,1)\)

为了维护转移,我们必然需要让\(dp(i,j)\)值从更小的树转移过来。

将根节点去掉,其子树构成一片大小为\(i-1\)的森林。

然而我们极度缺乏信息,所有子树构成一个集合,而元素间没有顺序关系。我们根本不知道有多少树,哪些树是同构的,难以转移。

不过我们可以分析的一点是,两两同构的子树容易导致重复的计算。

首先,同构的子树,大小必然相等。我们根据子树大小把原问题继续拆分,设\(tmp(p,j,t)\)表示大小为\(t\)的森林,每棵树大小不超过\(p\),其等价类大小的\(jk\)次方和,边界条件\(tmp(1,j,t)=1\)

\(f(p,i,j)\)表示\(i\)棵大小为\(p\)的树组成的森林的等价类大小的\(jk\)次方和。

枚举大小为\(i\)的树的个数进行转移。

\[dp(i,j)=tmp(i-1,j,i-1)\\ tmp(p,j,t)=\sum_{zp+v=t} tmp(p-1,j,v)f(p,z,j){t \choose zp}^{jk} \]

上面的式子应该不必多解释了吧。

现在问题的核心转移到了求解\(f(p,i,j)\)

怎么办呢?

\(Part 3\)

重新回顾\(f(p,i,j)\)的定义:\(f(p,i,j)\)表示\(i\)棵大小为\(p\)的树组成的森林的等价类大小的\(jk\)次方和。

大小为\(p\)的树可以划分为一些等价类,我们为这些等价类标号,那么对于这一片森林来讲,我们先定义一个\(\{ x \}\),表示总共\(i\)棵树,它们属于的等价类的标号组成的可重集,它可以转化为一个数列\(\{ a_1,a_2,\cdots,a_m\}\)(总共有\(m\)个等价类),\(a_i\)表示第\(i\)种树的个数,数列\(\{ x \}\)\(\{ a \}\)可确定一个森林类型。

由于\(f(p,i,j)\)维护的是森林,所以它是无序的,这里的无序是指树之间没有偏序关系,而点之间依然是有的(也就是有标号)。

现在我们再次给出一个定义\(f(\{ a \})\),表示在有序(各棵树之间的顺序)的情况下,类型为\(\{ a \}\)的森林的方案数。

得到等式:

\[f(p,i,j)=\sum_{ \{ a \} } {ip \choose p,p,p,\cdots,p}^{jk}f( \{ a \}) \prod_t \frac{1}{a_t!^{jk}}\\ =\frac{(ip!)^{jk}}{(p!)^{ijk}}\sum_{ \{ a \} } f( \{ a \}) \prod_t \frac{1}{a_t!^{jk}} \]

到现在为止,\(\{ a \}\)对我们来说仍然是一个确定的数列,我们下一步就是让数列\(\{ a \}\)抽象化,使其适用的范围扩大。

其实上面的式子已经为我们指出了一条路径\(\prod_t \frac{1}{a_t!}\)明显与数组内数的顺序无关,于是我们得到一个想法:把数列\(\{ a \}\)变为可重集\(\{ c \}\)

本质上,\(\{ c \}\)就是一个元素个数的可重集,也就是对原森林计算每个等价类的个数,这些数组成的可重集。在这个可重集中,每个数对应的树的类型都是不确定的。当然,也可以认为\(\{ c \}\)是一个有序序列,其中的数代表一个类型树的个数,而到底是什么类型的树我们还没有确定,上面的\(\{ x \}\)也可以当成有序序列来看(注:以下\(\{ x \},\{ a \},\{ c \}\)同时出现均代表同一片森林)。

同样给出定义,\(h(\{ c \})\)表示在有序的情况下,元素个数的可重集为\(\{ c \}\)的树的方案数。

\[f(p,i,j)=\frac{(ip!)^{jk}}{(p!)^{ijk}} \sum_{ \{ c \} } h( \{ c \}) \prod_t \frac{1}{c_t!^{jk}} \]

再来一个定义:

\[w(\{ c \})=\prod_t dp(p,c_tj) \]

由于这里所有的一切都是在\(f(p,i,j)\)的计算这个子问题下进行的,所以上式中的\(p,j\)\(f(p,i,j)\)中的\(p,j\)意义相同。

也就是我们为可重集\(\{ c \}\)中每一种元素钦定一棵树计算答案,但是\(h(\{ c \}) \ne w(\{ c \})\),原因在于\(h\)的计算中必须满足每个树对应的树的类型互不相同,而\(w\)的计算中没有这个限制。

我们需要利用群论利用\(w(\{ c \})\)解决\(h(\{ c \})\)的计算。

\(Part 4\)

我们首先必须明确的是,这里的置换群是什么,还有一个重要的点就是判断两个可重集\(\{ x \}\)本质不同的标准。需要注意的一点是,这里的群,并没有直接作用在可重集\(\{ x \}\)上,它只是一个计数工具。

\(1.\)群、置换:这里的置换就是普通的置换,即从一个排列置换到另一个排列,排列大小为树的个数\(i\),想必大家对这个群已经十分熟悉了。

\(2.\)等价标准:每个\(\{ x \}\)都会对应一个\(\{ c \}\)(经过上面\(\{ x \} \rightarrow \{ a \} \rightarrow \{ c \}\)的转换),如果\(\{ x_1 \},\{ x_2 \}\)对应\(\{ c_1 \},\{ c_2 \}\)相等,那么\(\{ x_1 \},\{ x_2 \}\)等价。这里的等价类与树的等价类是两种不同的体系,不要搞混。

设元素\(\{ x \}\)的等价类大小为\(Tr(\{ x \})\)

\[f(p,i,j)=\frac{(ip!)^{jk}}{(p!)^{ijk}} \sum_{ \{ c \} } h( \{ c \}) \prod_t \frac{1}{c_t!^{jk}}\\ =\frac{(ip!)^{jk}}{(p!)^{ijk}}\sum_{ \{ x \} \in X} \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{h(\{ c \})}{|Tr(\{ x \})|} \]

相当于每种等价类中的\(\{ x \}\)都计算一遍,每次的权重都是\(\frac{1}{|Tr(\{ x \})|}\),所以上式成立。

我们试图将\(\{ x \}\)与置换建立联系,对于\(s\)个相同的元素(把一堆相同元素拆成好几份也是合法的),我们对应到一个排列上的一个大小为\(s\)的循环,如果\(\{ x \}\)可以通过上述的对应关系对应到一个置换\(G_0\),那么我们称\(\{ x \}\)为置换\(G_0\)上的不动点,\(G_0\)的不动点集合为\(fix(G_0)\)

按照\(\operatorname{Pólya}\) 定理的方式,我们把置换\(T\)拆分成循环的形式,用数列\(\{ D_T \}\)表示,其中的数代表一个循环的大小。

考虑\(fix(T)\)中的元素,举个例子:

\(T\)中有一个循环的大小为\(3\),那么\(\{ x \}\)中对应的可以是\(3\)个相同的元素,也可以是\(5\)个相同元素中拆出\(3+2\)进行匹配。

也就是\(fix(T)\)可以兼容两堆树相同的情况,于是:

\[\sum_{\{ x \} \in fix(T)} h(\{ c \})=\prod_i dp(p,D_{T_i}j) \]

\(Part 5\)

一些奇怪的操作来了。

让置换带上权\(V\),试图去拟合求解的值。

\[\sum_{T \in G} V_T \sum_{\{ x \} \in fix(T)} h(\{ c \})\\ =\sum_{\{ x \} \in X} h(\{ c \}) \sum_{\{ x \} \in fix(T)} V_T\\ =\sum_{\{ x \} \in X} \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{h(\{ c \})}{|Tr(\{ x \})|}\\ \sum_{\{ x \} \in fix(T)} V_T=\left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{1}{|Tr(\{ x \})|} \]

为方便运算,添加一个常数\(C\)

\[\sum_{\{ x \} \in fix(T)} V_T=\left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{C}{|Tr(\{ x \})|} \]

等式右边存在连乘式,设:

\[V_T=\prod_t Q(D_{T_t}) \]

\[\sum_{\{ x \} \in fix(T)} \prod_t Q(c_t)=\left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{C}{|Tr(\{ x \})|} \]

对等式两边分开来推导。

\[|Tr(x)|=\frac{i!}{\prod_t c_t!}\\ 右边=C \left ( \prod_t \frac{1}{c_t!^{jk}} \frac{1}{{i \choose c_1,c_2,\cdots}} \right)\\ =\frac{C}{i!} \prod_s c_s! \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \\ \]

把左边的\(\{ x \} \in fix(T)\)拆开,我们主要考虑满足条件的\(T\)到底是什么。由置换圈定\(\{ x \}\),与从\(\{ x \}\)圈定置换的方式恰好相反,也就是\(\{ x \}\)中的\(3\)可以用可以用置换中的\(1+2\)对应,本质上是对\(\{ x \}\)中每个数的整数拆分,但是拆分之后还需要对于每个拆分的部分进行圆排列。

\[左边=\sum_{\{ x \} \in fix(T)} \prod_t Q(c_t)\\ =\prod_t \sum_{(\sum_{r=1}^{nr} e_r)=c_t} \frac{1}{nr!} {c_t \choose e_1,e_2,\cdots,c_{nr}} \left ( \prod_q (e_q-1)! \right ) \prod_q Q(e_q)\\ =\left ( \prod_s c_s! \right ) \sum_{(\sum_{r=1}^{nr} e_r)=c_t} \frac{1}{nr!} \prod_q \frac{Q(e_q)}{e_q} \]

经典的集合划分问题。

设:

\[F(x)=\sum_{i=1}^{\infty} \frac{Q(i)}{i} x^i \]

\[左边=\left ( \prod_s c_s! \right ) \prod_t [x^{c_t}]e^{F(x)}\\ =\frac{C}{i!} \prod_s c_s! \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \\ \prod_t [x^{c_t}]e^{F(x)}=\frac{C}{i!} \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \]

\(C=i!\)

\[\prod_t [x^{c_t}]e^{F(x)}=\prod_t \frac{1}{c_t!^{jk}}\\ [x^t]e^{F(x)}=\frac{1}{t^{jk}} \]

\(F_0(x)=\sum_{i=0}^{\infty} \frac{1}{i^{jk}}\)

\[F(x)=\ln F_0(x) \]

于是我们直接把所有\(Q(t)\)的值算出来了。

回到原问题,求解\(\sum_{\{ x \} \in X} \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{h(\{ c \})}{|Tr(\{ x \})|}\)

\[\sum_{\{ x \} \in X} \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{h(\{ c \})}{|Tr(\{ x \})|}\\ =\frac{1}{C} \sum_{T \in G} V_T \sum_{\{ x \} \in fix(T)} h(\{ c \})\\ =\frac{1}{C} \sum_{T \in G} \prod_t Q(c_t) dp(p,c_tj) \\ \]

\(T \in G\)拆开,把置换拆成多个循环的过程,也是整数拆分和圆排列的结合。

\[\frac{1}{C} \sum_{T \in G} \prod_t Q(c_t) dp(p,c_tj) \\ =\frac{1}{C} \sum_{(\sum_{r=1}^{nr} e_r)=i} \frac{1}{nr!} {i \choose e_1,e_2,\cdots,e_{nr}} \left ( \prod_q (e_q-1)! \right ) \prod_q Q(e_q)dp(p,e_qj)\\ =\frac{1}{C} i! \sum_{(\sum_{r=1}^{nr} e_r)=i} \frac{1}{nr!} \prod_q \frac{Q(e_q)dp(p,e_qj)}{e_q}\\ =\sum_{(\sum_{r=1}^{nr} e_r)=i} \frac{1}{nr!} \prod_q \frac{Q(e_q)dp(p,e_qj)}{e_q} \]

\(G(x)=\sum_{i=1}^{\infty} \frac{Q(i)dp(p,ij)}{i} x^i\)

\[\sum_{(\sum_{r=1}^{nr} e_r)=i} \frac{1}{nr!} \prod_q \frac{Q(e_q)dp(p,e_qj)}{e_q}\\ =[x^i] e^{G(x)} \]

\[f(p,i,j)=\frac{(ip!)^{jk}}{(p!)^{ijk}}\sum_{ \{ x \} \in X} \left ( \prod_t \frac{1}{c_t!^{jk}} \right) \frac{h(\{ c \})}{|Tr(\{ x \})|}\\ =\frac{(ip!)^{jk}}{(p!)^{ijk}} [x^i] e^{G(x)} \]

\(f(p,i,j)\)终于算完了。。。

\(Part 6\)

模数不一定是\(NTT\)模数,\(\ln\)\(\exp\)总不能\(MTT\)吧(害怕)。

\(\ln\)

\[G(x)=(\ln F(x))'\\ =\frac{F'(x)}{F(x)}\\ =F'(x)-(F(x)-1)G(x)\\ g_n=(n+1)f_{n+1}-\sum_{i=0}^{n-1} f_{n-i} g_{i}\\ \ln F(x)=\int G(x) dx \]

\(\exp\)

\[G(x)=\exp(F(x))\\ \ln G(x)=F(x)\\ \frac{G'(x)}{G(x)}=F'(x)\\ G'(x)=G(x)F'(x)\\ g_0=1,g_n=\frac{1}{n}\sum_{i=1}^n if_ig_{n-i} \]

\(Part 7\)

\(DP\)的过程为互相更新,更新顺序为\(dp \rightarrow f \rightarrow tmp \rightarrow dp \rightarrow \cdots\)

\(DP\)时间复杂度:

\[\sum_{p=1}^n \sum_{j=1}^{\frac{n}{p}} \sum_{i=1}^{\frac{n}{j}} \frac{i}{p}\\ =\sum_{j=1}^n \sum_{i=1}^{\frac{n}{j}} \sum_{p=1}^{\frac{n}{j}} \frac{i}{p}\\ \approx \sum_{j=1}^n \sum_{i=1}^{\frac{n}{j}} i \log i\\ =\sum_{i=1}^n i \log i \frac{n}{i}\\ =\sum_{i=1}^n n \log i\\ =O(n^2 \log n) \]

枚举\(j\),可以在\(\sum_{i=1}^n \frac{n^2}{i^2}=O(n^2)\)时间完成所有\(\ln\)的预处理。

\(\exp\)的复杂度:\(\sum_{i=1}^n \sum_{j=1}^{\frac{n}{i}} \frac{n^2}{(ij)^2}=O(n^2)\)

总时间复杂度:\(O(n^2 \log n)\)

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 2005
#define ll long long
using namespace std;
int n,k,mp;
int inv[N],fac[N],infac[N],kfac[N][N],kinfac[N][N];
int L[N][N],dp[N][N],f[N][N],tmp[N][N];
int ans,Q[N],g[N];
int r[N],cr[N];
void Add(int &x,int y)
{
    x=(x+y)%mp;
}
void Del(int &x,int y)
{
    x=(x-y)%mp;
}
void Mul(int &x,int y)
{
    x=(ll)x*y%mp;
}
int add(int x,int y)
{
    return (x+y)%mp;
}
int del(int x,int y)
{
    return (x-y)%mp;
}
int mul(int x,int y)
{
    return (ll)x*y%mp;
}
int ksm(int x,int y)
{
    int ans=1;
    while (y)
    {
        if (y & 1)
            Mul(ans,x);
        Mul(x,x);
        y >>=1;
    }
    return ans;
}
int C_j(int n,int k,int j)
{
    if (n<k)
        return 0;
    return mul(kfac[n][j],mul(kinfac[k][j],kinfac[n-k][j]));
}
void InvDev(int *a,int *b,int n)
{
    b[0]=0;
    for (int i=1;i<=n;++i)
        b[i]=mul(inv[i],a[i-1]);
    b[n+1]=0;
}
void GetLn(int *f,int *g,int n)
{
    f[n+1]=0;
    for (int i=0;i<n;++i)
    {
        cr[i]=mul(i+1,f[i+1]);
        for (int j=0;j<i;++j)
            Del(cr[i],mul(f[i-j],cr[j]));
    }
    InvDev(cr,g,n);
}
void GetExp(int *f,int *g,int n)
{
    g[0]=1;
    for (int i=1;i<=n;++i)
    {
        g[i]=0;
        for (int j=1;j<=i;++j)
            Add(g[i],mul(mul(j,f[j]),g[i-j]));
        Mul(g[i],inv[i]);
    }
}
int main()
{
    scanf("%d%d%d",&n,&k,&mp);
    inv[1]=fac[0]=fac[1]=infac[0]=infac[1]=1;
    for (int i=2;i<=n+2;++i)
        inv[i]=mul(mp-mp/i,inv[mp%i]),fac[i]=mul(fac[i-1],i),infac[i]=mul(infac[i-1],inv[i]);
    for (int i=0;i<=n+2;++i)
        kfac[i][1]=ksm(fac[i],k),kinfac[i][1]=ksm(infac[i],k);
    for (int i=0;i<=n+2;++i)
        for (int j=2;j<=n;++j)
            kfac[i][j]=mul(kfac[i][j-1],kfac[i][1]),kinfac[i][j]=mul(kinfac[i][j-1],kinfac[i][1]);
    for (int j=1;j<=n;++j)
    {
        for (int i=0;i<=n/j;++i)
            r[i]=kinfac[i][j];
        GetLn(r,L[j],n/j);
    }
    for (int i=1;i<=n;++i)
        dp[1][i]=1;
    for (int j=1;j<=n;++j)
        for (int i=0;i<=n/j;++i)
            tmp[i][j]=1;
    for (int p=2;p<=n;++p)
    {
        for (int j=1;j<=n/p;++j)
            dp[p][j]=tmp[p-1][j];
        for (int j=1;j<=n/p;++j)
        {
            int cnt=n/p/j,sz=n/j;
            for (int i=0;i<=cnt;++i)
                Q[i]=L[j][i];
            for (int i=1;i<=cnt;++i)
                g[i]=mul(Q[i],dp[p][i*j]);
            GetExp(g,r,cnt);
            for (int i=1;i<=cnt;++i)
                f[i][j]=mul(mul(kfac[i*p][j],kinfac[p][i*j]),r[i]);
            for (int i=0;i<=sz;++i)
                r[i]=tmp[i][j];
            for (int i=1;i<=sz;++i)
                for (int z=1;z<=i/p;++z)
                    Add(r[i],mul(mul(tmp[i-z*p][j],f[z][j]),C_j(i,z*p,j)));
            for (int i=0;i<=sz;++i)
                tmp[i][j]=r[i];
        }
    }
    ans=mul(dp[n][1],kinfac[n-1][1]);
    ans=(ans%mp+mp)%mp;
    printf("%d\n",ans);
    return 0;
}
posted @ 2021-02-01 18:38  GK0328  阅读(124)  评论(1编辑  收藏  举报