题解 CF830D 【Singer House】

猪脑子

2020-06-05 15:59:44

Solution

首先,对于一个n,我们思考一下如何求解这个n的路径条数,不妨设其为$f_n$。 一个思路是考虑这条路径是否经过根节点,那么有几种情况: 这条路径只包含根节点;这条路径从下面某棵子树内一条路径连上来,再连接下去 乍一看似乎能做,但是我们会发现,从一棵子树中连上来的路径可能会连回同一棵子树,那么如果我们要算$f_n$,就必须算出从深度为n-1的子树内选择两条不相交的路径的方案数$g_{n-1}$。 你可能会想继续讨论$g_n$的方案数,但是你会发现,你要算$g_n$,还得算深度为n-1的树种选三条不相交路径的方案数…… 既然如此,~~我们观察一下数据范围,~~ 不妨多设一维状态: 令$f_{n,k}$代表在深度为$n$的树中选择$k$条不相交路径的方案数。 看上去似乎变难了,毕竟原题只让我们求一条路径的方案数。 但是我们发现,这个“加强”版本似乎更好做了,因为转移变得十分简单: $$f_{n,k}=\sum_{i+j=k-1}f_{n-1,i}\times f_{n-1,j}$$ $$+\sum_{i+j=k}f_{n-1,i}\times f_{n-1,j}$$ $$+\sum_{i+j=k}f_{n-1,i}\times f_{n-1,j}\times (2k)$$ $$+\sum_{i+j=k+1}f_{n-1,i}\times f_{n-1,j}\times (k+1)\times k$$ 这四种情况分别是:根节点单独形成一条链、根节点不属于任何一条链、根节点与左右子树内某条链连在一起(分从链的尾端连上来和连到链的开头两种情况)、还有根节点从某条链上连上再连到另一条链上去。 边界条件也很显然,这里就不写了。 代码如下,我写的是记忆化搜索 ```cpp #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int Mod=1000000007; int f[410][410]; int F(int n,int k) { if(!k)return 1; if(n==1){ return k==1; } if(f[n][k]!=-1)return f[n][k]; f[n][k]=0; for(int i=0;i<=k;i++) f[n][k]=(f[n][k]+1ll*F(n-1,i)*F(n-1,k-i))%Mod; for(int i=0;i<k;i++) f[n][k]=(f[n][k]+1ll*F(n-1,i)*F(n-1,k-i-1))%Mod; for(int i=0;i<=k;i++) f[n][k]=(f[n][k]+1ll*F(n-1,i)*F(n-1,k-i)%Mod*k*2)%Mod; for(int i=0;i<=k+1;i++) f[n][k]=(f[n][k]+1ll*F(n-1,i)*F(n-1,k+1-i)%Mod*(k+1)*k)%Mod; return f[n][k]; } int main() { int n;scanf("%d",&n); memset(f,-1,sizeof(f)); printf("%d\n",F(n,1)); return 0; } ``` ~~好不容易自己搞出2800的题,而且还没有别人发题解,怎么着也得水一下啊对不对~~