Part0. 前言
好简单的 Div.3 T4。
Part1. 题意
给定一个 n × m n\times m n×m 的网格图,在其中构建一棵有根树,使得父节点一定在子节点上方,求可构建多少棵不同的有根树。
Part2. 思路
对于点 A ( i , j ) A (i,j) A(i,j),我们发现只要点 B ( x , y ) B (x,y) B(x,y) 满足 x < i x<i x<i,即可将 B B B 作为 A A A 的父节点。根据这一性质,我们发现对于第 i i i 行的所有点, 1 ∼ i − 1 1\sim i-1 1∼i−1 行的所有点均可作为其父节点,而不用管位置关系。
因此,可设 f i , j f_{i,j} fi,j 表示当前考虑第 i i i 行、第 1 ∼ i 1\sim i 1∼i 行共有 j j j 个点在树上。因为每一行都能作为根节点所在的行,所以初始时 f i , 1 = m f_{i,1}=m fi,1=m。
考虑转移。对于第 i i i 行、第 1 ∼ i − 1 1\sim i-1 1∼i−1 行共有 j j j 个点在树上、第 i i i 行有 k k k 个点在树上的情况, f i , j + k f_{i,j+k} fi,j+k 由 f i − 1 , j f_{i-1,j} fi−1,j 转移而来、第 i i i 行的 k k k 个在树上的点可任选、其父节点也可任选,我们得到转移方程:
f i , j + k = ∑ j = 0 1 + ( i − 2 ) × m ∑ k = 0 m f [ i − 1 ] [ j ] × ( m k ) × j k f_{i,j+k}=\sum\limits_{j=0}^{1+(i-2)\times m}\sum\limits_{k=0}^{m} f[i-1][j]\times\binom{m}{k}\times j^k fi,j+k=j=0∑1+(i−2)×mk=0∑mf[i−1][j]×(km)×jk
最终答案为 ∑ i = 1 n × m f n , i \sum\limits_{i=1}^{n\times m} f_{n,i} i=1∑n×mfn,i。
Part3. 解释
枚举时 1 ≤ j ≤ 1 + ( i − 2 ) × m 1\le j\le 1+(i-2)\times m 1≤j≤1+(i−2)×m 是因为 1 ∼ i − 1 1\sim i-1 1∼i−1 行必有一行只有一个根节点,其它行不定,所以不用枚举至 ( i − 1 ) × m (i-1)\times m (i−1)×m 。
Part4. 代码
为和思路保持一致,使用赛后修改的代码。
#include <bits/stdc++.h>
using namespace std;
namespace rab {
const int N=81,mod=1e9+7;
int n,m,ans,c[N][N],f[N][N*N];
long long g;
inline int ksm (int x,int y,int z=mod) {
int rs=1;
while (y) {
if (y&1) rs=1ll*rs*x%z;
x=1ll*x*x%z,y>>=1;
}
return rs;
}
inline int main () {
cin>> n>> m;
for (int i=0;i<=m;i++) {
c[i][0]=1;
for (int j=1;j<=i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
}
for (int i=1;i<=n;i++) f[i][1]=m;
for (int i=2;i<=n;i++) {
for (int j=0;j<=1+(i-2)*m;j++) {
for (int k=0;k<=m;k++) {
g=1ll*f[i-1][j]*c[m][k];
if (g>=mod) g%=mod;
g=1ll*g*ksm (j,k);
if (g>=mod) g%=mod;
f[i][j+k]=(f[i][j+k]+g)%mod;
}
}
}
for (int i=1;i<=n*m;i++) ans=(ans+f[n][i])%mod;
cout<< ans;
return 0;
}
}
int main () {
ios::sync_with_stdio (0);
cin.tie (0);cout.tie (0);
return rab::main ();
}
完结撒花!