题目:http://codeforces.com/problemset/problem/431/C
题意:给出K-Tree定义,每个结点都有恰好K个孩子,这棵树无限增长。每个节点到它K个孩子的K条边的权重刚好是1,2,3...,K(看图应该也看得明白)
现在问有多少条路径,使得从根节点出发到达某个结点,经过的边权重之和恰好为n,并且经过的边至少有一条权重不小于d。
先不考虑d。其实可以发现每个节点选择一条边到达一个子节点之后,假设经过的权值为j,那么问题就是原问题的n变成n-j而已。
记dp[n]为答案,那么dp[n] = dp[n-1]+dp[n-2]+...+dp[n-k]。n和k只有100,直接计算问题不大。
再来考虑d,其实可以转换问题,求出所有边权重小于d的,再用总方案数减掉。而所有权重小于d的话,问题等价于这棵树是K值等于d-1的K-Tree,然后还是求n的方案数。
然后小心取模就没问题了。
这里说下,我的实现中dp[0][i]代表K值为k,权值和为i的方案数;dp[1][i]代表K值为d-1,权值和为i的方案数;
#include<cstdio> #include<cstring> #define LL long long #define mod 1000000007 LL dp[2][101]; int n, k, d, i, j; int main(){ while(~scanf("%d %d %d", &n, &k, &d)){ memset(dp,0,sizeof(dp)); dp[0][0]=dp[1][0]=1; d--; for(i=1; i<=n; i++){ for(j=1; j<=k; j++){ if(i-j<0) break; dp[0][i] = (dp[0][i]+dp[0][i-j])%mod; } for(j=1; j<=d; j++){ if(i-j<0) break; dp[1][i] = (dp[1][i]+dp[1][i-j])%mod; } } printf("%I64d\n", ((dp[0][n]-dp[1][n])%mod + mod)%mod); } return 0; } #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; long long dp[105][105][105]; const long long mod= 1000000007; int main() { int m,n,d; while(~scanf("%d%d%d",&m,&n,&d)) { memset(dp,0,sizeof(dp)); dp[0][0][0]=1; for(int c=0;c<=m;c++) for(int i=1;i<=n;i++) { for(int j=m;j>=0;j--) { for(int k=0;k<=n;k++) { if(dp[c][j][k]!=0&&(j+i)<=m) { int cur=k; if(i>k) cur=i; dp[c+1][j+i][cur]=(dp[c+1][j+i][cur]+dp[c][j][k])%mod; } } } } long long ans=0; for(int c=0;c<=100;c++) for(int k=d;k<=n;k++) ans=(ans+dp[c][m][k])%mod; printf("%lld\n",ans); } }