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 */
Zeit und Raum trennen dich und mich.时空将你我分开。