NOIP模拟 超级树

思路:首先,别说这个题状态难想,先尝试做一下,dp[i]代表i-超级树的路径数,那么可以直接进行转移的考虑,也不要去看题中给的那个复杂的图,只需要画出两坨子树和一个根节点,我们假设子树中路径数已经推出来,那么我们可以得到什么?首先,设sum=dp[i-1]^2

1.两棵子树中原封不懂,直接有sum种,

2.根节点单独算一条,有sum种,

3.左->根||根->左,任意选一个与根相连,2*dp[i-1]*dp[i-1]?出问题了。。是把两条路连在一起了还是本来就是一条路我又加了一条路?

转移进行到这里就停止了,这也就是许多题解没有的东西,如何定义状态,那么我们现在分不清到底从哪转移到哪儿,所以我们可以把状态再细分一下,我在组合数学专题里也有所感悟,DP求方案数,就是把所谓的许多方案按状态分类,在按阶段之间的联系去转移。那么就可以得到答案,现在的状态分类还是比较混乱的,一个状态里鱼龙混杂导致难以转移,因此我们把他们路径都定义为互不相交的,即无公共点的,但这样肯定是不全的,就比如说原问题有许多相交的路径,那么这样定义就即使能递推也没法得到答案,再结合数据范围,我们想到再在状态后边加一维,代表什么呢,我要选出j条不相交的路径有几种情况,这样在3的转移中就能用到几条不相交的方案就能得到,岂不美哉。。。然后这题就比较简单了,如果转移仍然有困难的同学去DeepinC的题解看一看,超详细的!!!还附带卡常技巧

 

#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int K=320;
long long f[K][K];
int rd()
{
    char cc=getchar();
    long long s=0,w=1;
    while(cc<'0'||cc>'9') {if(cc=='-') w=-1;cc=getchar();}
    while(cc>='0'&&cc<='9') s=(s<<3)+(s<<1)+cc-'0',cc=getchar();
    return s*w;
}
int main()
{
    const int n=rd(),mod=rd();
    f[1][1]=f[1][0]=1%mod;
    for(register int i=1;i<=n;i++)
    {
        int kk=min(n-i+2,i<=9?1<<i:300);
        for(register int j=0;j<=kk+1;j++)
        {
            for(register int k=0;k<=kk+1;k++)
            {
                if(j+k>n+1)break;
                const register int sum=1ll*f[i][j]*f[i][k]%mod;
                f[i+1][j+k]=(f[i+1][j+k]+1ll*sum*(2*j+2*k+1));
                f[i+1][j+k+1]=(f[i+1][j+k+1]+sum);
                f[i+1][j+k-1]=(f[i+1][j+k-1]+1ll*sum*(j+k)*(j+k-1))%mod;
            }
        }
    }
    printf("%lld\n",f[n][1]);
}
/*
g++ 1.cpp -o 1
./1
3 100
*/
/*
g++ 1.cpp -o 1
./1
1 1
*/
View Code

 

posted @ 2019-07-23 06:21  starsing  阅读(190)  评论(0编辑  收藏  举报