SP1480题解

《四重计树法》

  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));
		}
	}
}
posted @ 2022-03-02 18:51  Prean  阅读(38)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};