zoukankan      html  css  js  c++  java
  • CF724F 题解

    原题链接

    题目大意

    如果两棵树可以通过重标号后变为完全相同,那么它们就是同构的。
    将中间节点定义为度数大于(1)的节点。
    计算由(n)个节点,其中所有的中间节点度数都为(d)的互不同构的树的数量。
    答案对大质数取模。

    (Solution)

    解决问题的重点在于如何判断树是否同构,然鹅由于无根树是可以重标号(换根)的,所以我们需要对于每棵树找出一个特殊的根,将无根树转化为有根树

    我们不妨将重心视为一棵树的根,而重心有一个至关重要的性质:任意字数大小不超过(n/2)(但是对于有两个重心的情况我们在后面加以讨论)

    在确定了根以后,我们考虑如何统计方案数

    因为这是dp专题里面的题,我们令(f_{i,j,k})表示一棵树有(i)个节点,(j)棵子树(那么此子树的根的度数即为(j+1)),每棵子树节点数都不超过(k)的有根树数量

    若所有子树的大小均(leq k),显然有(f_{i,j,k})+=(f_{i,j,k-1})

    那么剩下的情况即为所有子树的大小均为(k),这时出现了一个问题:

    设当前树的根为(u),那么对于它的两棵子树(son_1,son_2),若两棵子树的(size)相同(均为(k)),此时(son_1)选择了方案(f_1)(son_2)选择了方案(f_2),然后(son_1)选择了方案(f_2)(son_2)选择了方案(f_1)时,由于两棵子树(size)相同,此时就出现了两棵同构的树,所以我们必须枚举大小为(k)的子树个数,由于上一种情况中(f_{i,j,k-1})是一个已经考虑过的子问题,现在的问题被我们转化为:你有(t)(size)均为(k)的子树,求将这些子树组合成一棵大树(?)的方案数,子树间互不区分

    那么我们(sumlimits_{t=1})来枚举有多少(size)均为(k)的子树,那么有:

    (f_{i,j,k})+=(sumlimits_{t=1}^{}{f_{i-tk,j-1,k-1} imes { {f_{k,d-1,k-1}+t-1} choose t}}(tk leq i , t leq k))

    其中(x+t-1 choose t)表示在(x)种方案中不分顺序地选取(t)种,可重复的方案数。

    有了(dp)的式子后,我们来统计答案:

    回顾一下,我们现在还有双重心的情况没有研究,显然,双重心存在当且仅当(2|n)

    那么若(n)为奇数,答案即为(f_{n,d,leftlfloorfrac{n}{2} ight floor})

    (n)为偶数,还要减掉双重心的情况(在上面被算了两次),双重心的情况个数即为(f_{frac{n}{2},d-1,frac{n}{2}} choose 2)

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    namespace my_std
    {
    	typedef long long ll;
    	typedef double db;
    	#define pf printf
    	#define pc putchar
    	#define fr(i,x,y) for(register ll i=(x);i<=(y);i++)
    	#define pfr(i,x,y) for(register ll i=(x);i>=(y);i--)
    	#define go(x) for(ll i=head[u];i;i=e[i].nxt)
    	#define enter pc('
    ')
    	#define space pc(' ')
    	#define fir first
    	#define sec second
    	#define MP make_pair
    	const ll inf=0x3f3f3f3f;
    	const ll inff=1e15;
    	inline ll read()
    	{
    		ll sum=0,f=1;
    		char ch=0;
    		while(!isdigit(ch))
    		{
    			if(ch=='-') f=-1;
    			ch=getchar();
    		}
    		while(isdigit(ch))
    		{
    			sum=(sum<<1)+(sum<<3)+(ch^48);
    			ch=getchar();
    		}
    		return sum*f;
    	}
    	inline void write(ll x)
    	{
    		if(x<0)
    		{
    			x=-x;
    			pc('-');
    		}
    		if(x>9) write(x/10);
    		pc(x%10+'0');
    	}
    	inline void writeln(ll x)
    	{
    		write(x);
    		enter;
    	}
    	inline void writesp(ll x)
    	{
    		write(x);
    		space;
    	}
    }
    using namespace my_std;
    const ll N=1050;
    ll n,d,mod,f[N][20][N],inv[20],mul[20]; 
    inline ll ksmod(ll a,ll b)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=(ans*a)%mod;
    		}
    		a=(a*a)%mod;
    		b>>=1;
    	}
    	return ans;
    }
    inline ll C(ll n,ll m,ll ans=1)
    {
    	if(m>n||m<0||n<0) return 0;
    	fr(i,1,m) ans=ans*(n-i+1)%mod;
        ans=ans*inv[m]%mod;
        return ans;
    }
    inline void init()
    {
    	mul[0]=inv[0]=1;
        for(ll i=1;i<=10;i++) mul[i]=(mul[i-1]*i)%mod;
        inv[10]=ksmod(mul[10],mod-2);
        for(ll i=10-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
    }
    int main(void)
    {
    	n=read(),d=read(),mod=read();
    	if(n==1||n==2)
    	{
    		puts("1");
    		return 0;
    	}
    	init();
    	fr(i,0,n) f[1][0][i]=1;
    	fr(i,2,n) for(ll j=1;j<i&&j<=d;j++) fr(k,1,n)
    	{
    		f[i][j][k]=f[i][j][k-1];
    		for(ll t=1;t*k<i&&t<=j;t++)
    		{
    			if(k!=1) f[i][j][k]=(f[i][j][k]+f[i-t*k][j-t][k-1]*C(f[k][d-1][k-1]+t-1,t)%mod)%mod;
    			else f[i][j][k]=(f[i][j][k]+f[i-t*k][j-t][k-1]*C(f[k][0][k-1]+t-1,t)%mod)%mod;
    		} 
    	}
    	ll ans=f[n][d][n/2];
    	if(!(n&1)) ans=(ans-C(f[n/2][d-1][n/2],2)+mod)%mod;
    	writeln(ans);
    	return 0;
    }
    
  • 相关阅读:
    Arduino单片机使用和开发问题记录
    U盘启动笔记本无法安装Win7问题和解决
    FreeSwitch安装配置记录
    TOGAF有哪些学习要点?
    有没有方法指导IT顶层规划和实施?
    专业品质的3月TOGAF认证线上公开课
    《方法论学习 v0.8》出炉
    记疫情期间第一次TOGAF认证课:从线下转到线上教育
    TOGAF认证课程,作为讲师我有话说
    首个企业架构TOGAF角色扮演案例培训的诞生
  • 原文地址:https://www.cnblogs.com/lgj-lgj/p/12628474.html
Copyright © 2011-2022 走看看