zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    windows7触屏编程
    改变窗口大小,恢复以前的大小
    insert()
    index()
    help()
    id()
    extend()
    count()
    cmp()
    append()
  • 原文地址:https://www.cnblogs.com/gmh77/p/12459207.html
Copyright © 2011-2022 走看看