C. k-Tree
题目大意:
给你一个K树,K树的定义:
每个节点都有K个孩子,每条树边都有权值,并且树边权值是从1-k排布的,如题干中图片所示。
然后让你找一共有多少条路,使得从根节点出发,走完这条路之后,权值总和为n,并且保证至少有一条树边的权值大于等于d、答案对1e9+7取膜。
思路;
1、经典的dp统计方案数的类型题。我们设定dp【i】【j】【2】,其中dp【i】【j】【0】表示当前走到第i层,前i层的树边总和为j,并且还没有一条树边的权值大于等于d的方案数。那么dp【i】【j】【1】表示当前走到第i层,前i层树边综合为j,并且右至少一条树边的权值大于等于d的方案数。
2、那么不难想到其状态转移方程:
①dp【i】【j】【1】+=dp【i-1】【k】【1】(k<j)
表示走到第i层,选择走的路的权值为j-k,并且这条路上至少有一条树边的权值大于等于d的方案数的状态转移。
然后我们讨论当前选择的这条路的权值j-k所带来的变化:
②dp【i】【j】【0】+=dp【i-1】【k】【0】(j-k<d)
如果当前选择的这条路是小于d的,那么其当前走到了第i层权值为j的不存在一条树边大于等于d的方案数,只能从走到第i-1层权值和为k的不存在一条树边大于等于d的方案数转移过来。
③dp【i】【j】【1】+=dp【i-1】【k】【0】(j-k>=d)
如果当前选择的这条路是大于等于d的,那么其当前走到了第i层权值为j的情况,就是一个包含了树边大于等于d的情况,那么走到第i-1层权值和为k的不存在一条树边大于等于d的状态就可以转移到走到第i层权值和为j的存在一条树边大于等于d的状态上来。
3、注意取膜。
Ac代码:
#include<stdio.h>
#include<string.h>
using namespace std;
int mod=1e9+7;
int dp[150][150][2];
int main()
{
int n,k,d;
while(~scanf("%d%d%d",&n,&k,&d))
{
memset(dp,0,sizeof(dp));
dp[0][0][0]=1;
for(int i=1;i<=100;i++)
{
for(int j=0;j<=n;j++)
{
for(int kk=0;kk<j;kk++)
{
int tmp=j-kk;
if(tmp>k)continue;
if(tmp>=d)
{
dp[i][j][1]+=dp[i-1][kk][0];
dp[i][j][1]%=mod;
dp[i][j][1]+=dp[i-1][kk][1];
dp[i][j][1]%=mod;
}
else
{
dp[i][j][0]+=dp[i-1][kk][0];
dp[i][j][0]%=mod;
dp[i][j][1]+=dp[i-1][kk][1];
dp[i][j][1]%=mod;
}
}
}
}
int ans=0;
for(int i=1;i<=100;i++)
{
ans+=dp[i][n][1];
ans%=mod;
}
printf("%d\n",ans);
}
}