题意:
分析:
考虑如何求方案数
dp[i][j]表示i个数字的和为j的方案数,这是个经典问题,转移有两种,一个是填一个数字1,一个是整体加1
然后这个问题并不是求方案数,而是求对应的权值和
我们很容易想到dp[i][j]维护对应的m个下降幂Σx^i,最后再用斯特林数还原成m次幂
但这样时间复杂度是O(nmk)的,无法接受
题解给出了一个很妙的想法,我们去计算每个数字对答案的贡献,我们只关心这个数字出现的次数,不妨设我们现在考虑数字x
x出现总次数={x恰好出现一次的方案数}*1+{x恰好出现两次的方案数}*2+......
这样无法求解
但可以转换成这样:
x出现总次数={x至少出现一次的方案数}+{x至少出现两次的方案数}+.......
=dp[k-1][n-x]+dp[k-2][n-2x]+dp[k-3][n-3x]+.......
这样就可以O(nk)解决了
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=4096,mod=1e9+7; 4 int dp[maxn+5][maxn+5]; 5 int n,k,m; 6 int ans=0; 7 void inc(int &a,int b) 8 { 9 a=(a+b)%mod; 10 } 11 int Pow(long long a,int b) 12 { 13 long long ans=1; 14 while(b) 15 { 16 if(b&1) ans=ans*a%mod; 17 a=a*a%mod; 18 b>>=1; 19 } 20 return ans; 21 } 22 int main() 23 { scanf("%d%d%d",&n,&k,&m); 24 dp[0][0]=1; 25 for(int i=1;i<=k;++i) 26 for(int j=1;j<=n;++j) 27 { 28 if(j>=1) 29 inc(dp[i][j],dp[i-1][j-1]); 30 if(j>=i-1) 31 inc(dp[i][j],dp[i][j-i]); 32 } 33 for(int i=1;i<=n;++i) 34 { 35 int s=0; 36 for(int j=1;j<=k&&i*j<=n;++j) 37 inc(s,dp[k-j][n-i*j]); 38 inc(ans,1LL*s*Pow(1LL*i,m)%mod); 39 } 40 printf("%d ",ans); 41 return 0; 42 }