CS Academy #32 G

题意:

  

分析:

  考虑如何求方案数

  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\n",ans);
41     return 0;
42 }
View Code

 

posted @ 2018-03-24 12:23  Chellyutaha  阅读(335)  评论(0编辑  收藏  举报