6494. 【GDOI2020模拟03.08】勘探

题目描述


题解

不是题解做法

生成树计数问题一般考虑统计重心,然后判掉两个重心的情况

设f[i][j]表示大小为i深度为j的个数,满足任何时候最长链<=L,然后容斥得到=L的答案

由于两个重心只有n为偶数时才可能,因此每次加入的子树大小不超过(n-1)/2,最后考虑偶数的情况

先加入深度为i-1的子树,按照大小顺序加入,之后每次按大小顺序加入深度<i-1的所有可能的子树,当顺序确定下来之后就不会算重

设同一大小当前可能深度的子树共有n种,要放m个这样大小的子树,那么方案数(无标号)为

(sum_{i=1}^{m}{C_n^i*C_{m-1}^{i-1}}=C_{n+m-1}^m)(范德蒙恒等式)

这个可以暴力算or递推,也可以预处理

因为有枚举子树大小和子树个数,这个是n ln n的,所以时间复杂度为O(n^3 ln n)

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a<b?a:b)
#define ll long long
#define file
using namespace std;

ll f[201][201],g[201][201],F[201][201][201],G[201][201][201],w[201],ans;
int n,L,mod,i,j,k,l;

void dp(ll type)
{
	int i,j,k,l;
	ll Ans=0;
	
	if (L<0) return;
	
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	memset(G,0,sizeof(G));
	memset(F,0,sizeof(F));
	
	fo(i,1,n)
	f[0][1]=g[0][1]=F[0][1][i]=G[0][1][i]=1;
	
	fo(i,1,L)
	{
		f[i][1]=1;
		fd(k,(n-1)/2,1)
		{
			fd(j,n-k,1)
			if (f[i][j])
			{
				if (i+i<=L)
				{
					fd(l,(n-j)/k,1)
					f[i][j+k*l]=(f[i][j+k*l]+f[i][j]*F[i-1][k][l])%mod;
				}
				else
				if (j==1)
				f[i][j+k]=(f[i][j+k]+f[i-1][k])%mod;
			}
		}
		f[i][1]=0;
		
		if (i>1 && i<L)
		{
			fd(k,(n-1)/2,1)
			{
				fd(j,n-k,2)
				if (f[i][j])
				{
					fd(l,(n-j)/k,1)
					f[i][j+k*l]=(f[i][j+k*l]+f[i][j]*G[min(L-i-1,i-2)][k][l])%mod;
				}
			}
		}
		
		fo(j,1,n)
		{
			l=n/j;
			g[i][j]=(g[i-1][j]+f[i][j])%mod;
			
			F[i][j][1]=f[i][j];
			G[i][j][1]=g[i][j];
			fo(k,2,l)
			{
				F[i][j][k]=F[i][j][k-1]*(f[i][j]+k-1)%mod*w[k]%mod;
				G[i][j][k]=G[i][j][k-1]*(g[i][j]+k-1)%mod*w[k]%mod;
			}
		}
	}
	
	Ans=g[L][n];
	if (!(n&1))
	{
		fo(i,1,L-2)
		Ans=(Ans+(i>1)*f[i][n/2]*G[min(L-i-1,i-1)][n/2][1]+(i+i+1<=L)*F[i][n/2][2])%mod;
	}
	ans=(ans+Ans*type)%mod;
}

int main()
{
	freopen("exploit.in","r",stdin);
	#ifdef file
	freopen("exploit.out","w",stdout);
	#endif
	
	scanf("%d%d%d",&n,&L,&mod);
	if (L==n)
	{
		printf("0
");
		return 0;
	}
	
	w[1]=1;
	fo(i,2,n)
	w[i]=mod-w[mod%i]*(mod/i)%mod;
	
	dp(1);
	--L;
	dp(-1);
	
	printf("%lld
",(ans+mod)%mod);
	
	fclose(stdin);
	fclose(stdout);
	
	return 0;
}
原文地址:https://www.cnblogs.com/gmh77/p/12459207.html