SP1480题解
阅读原文时间:2023年07月08日阅读:1

《四重计树法》

  1. 有标号无根

prufer 序列,\(n^{n-2}\)。

  1. 有标号有根

prufer 序列,\(n^{n-1}\)。

  1. 无标号有根

设 \(f[n]\) 为 \(n\) 个节点时的答案,有:

\[f[n]=\sum_{k=1}^n\frac{[\sum_{i=1}^ks_i=n-1]\prod_{i=1}^kf[s_i]}{k!}
\]

人话就是 \(F(x)=x\times Euler(F(x))\)。

考虑求导列出 ODE 然后 \(O(n^2)\)。

\[F(x)=x\exp(\sum_{i=0}\frac{F(x^i)}{i})
\]

求导:

\[F'(x)=Euler(F(x))+x(Euler(F(x))(\sum_{i=1}\frac{F(x^i)}{i}{\rm d}x))
\]

\[F'(x)=Euler(F(x))+x(Euler(F(x))(\sum_{i=1}\frac{ix^{i-1}F'(x^i)}{i}))
\]

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

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

\[F(x)=x(F'(x)-F(x)(\sum_{i=1}x^{i-1}F(x^i)))
\]

\[F(x)=xF'(x)-F(x)(\sum_{i=1}x^iF(x^i))
\]

同理,只需要维护出 \(F(x)\) 就可以维护出 \(F'(x),\sum_{i=1}x^iF(x^i)\) 和后面那个卷积,复杂度 \(O(n^2)\)。

  1. 无标号无根

考虑把每颗无根树变成 以重心为根的有根树

然后我们只需要做一遍有根的再减去不为重心的即可。

不为重心,那么一定有一个子树的大小大于了 \(\lfloor\frac{n}{2}\rfloor\)。

当 \(n\) 为奇数时,每棵树只有一个重心,减去的方案数为:

\[\sum_{i=\lfloor\frac{n}{2}\rfloor+1}^{n-1}f_i\times f_{n-i}
\]

当 \(n\) 为偶数时,有些树可能有两个重心。因此还需要额外减去有两个重心的树。

有两个重心就说明有一个分界的边,两边都恰好有 \(\frac{n}{2}\) 个节点。从 \(f_{\frac{n}{2}}\) 中选出两个方案即可。也就是 \(\binom{f_{\frac{n}{2}}}{2}\)。

#include<cstdio>
const int M=1005;
inline void swap(int&a,int&b){
    int c=a;a=b;b=c;
}
inline int pow(int a,int b,const int&mod){
    int ans(1);for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)ans=1ll*ans*a%mod;return ans;
}
inline int Solve1(const int&n,const int&mod){
    static int f[M],g[M],inv[M];
    int ans(0);
    f[1]=g[1]=inv[1]=1;
    for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=2;i<=n;++i){
        for(int k=1;k<=i;++k)f[i]=(f[i]+1ll*f[k]*g[i-k])%mod;
        f[i]=1ll*f[i]*inv[i-1]%mod;
        for(int k=1;k<=i;++k)if(!(i%k))g[i]=(g[i]+1ll*f[k]*k)%mod;
    }
    ans=f[n];
    for(int i=0;i<=n;++i)f[i]=g[i]=inv[i]=0;
    return ans;
}
inline int Solve2(const int&n,const int&mod){
    static int f[M],g[M],inv[M];
    int ans(0);
    f[1]=g[1]=inv[1]=1;
    for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=2;i<=n;++i){
        for(int k=1;k<=i;++k)f[i]=(f[i]+1ll*f[k]*g[i-k])%mod;
        f[i]=1ll*f[i]*inv[i-1]%mod;
        for(int k=1;k<=i;++k)if(!(i%k))g[i]=(g[i]+1ll*f[k]*k)%mod;
    }
    ans=f[n];
    for(int i=n/2+1;i<n;++i)ans=(ans-1ll*f[i]*f[n-i])%mod;
    if(!(n%2))ans=(ans-1ll*f[n/2]*(f[n/2]-1)/2%mod)%mod;
    for(int i=0;i<=n;++i)f[i]=g[i]=inv[i]=0;
    return(ans+mod)%mod;
}
signed main(){
    int t,n,mod;
    while(~scanf("%u%u%u",&t,&n,&mod)){
        if(t==1){
            printf("%u\n",n==1?1:pow(n%mod,(n-2)%(mod-1),mod));
        }
        if(t==2){
            printf("%u\n",n==1?1:pow(n%mod,(n-1)%(mod-1),mod));
        }
        if(t==3){
            printf("%u\n",Solve1(n,mod));
        }
        if(t==4){
            printf("%u\n",Solve2(n,mod));
        }
    }
}