单身三连之一

一个让单身狗们崩溃的题……

题目大意:

  有N件物品,一共取D次,一次取的必须少于M件,问共有多少种取法。(每个物品相同,有多测,对998244353取模)

题解:

  30%算法(N,D<=20,M<=10)

    简单的DP。

    设f[i][j]为取了i次,共取了j件物品的方案数,则有如下状态转移方程:

      f[i][j]=∑k<jk=max(j-m,0)f[i-1][k]

    初状态f[0][0]=1,末状态为f[d][n]。

    时间复杂度O(NMD)。

  100%算法(N,M<=2000,D<=1012

    可以看出D大的吓人,我们不可能枚举D。

    我们首先会想到矩阵快速幂,但是此题的矩阵并不特殊,短时间找规律很难,不易优化成n2级别,但n3的复杂度又承受不起。

    我们发现,相对于D,N很小,也就是说最多有N天能取到东西,剩下的天数如同虚设。

    我们只考虑这N天内的情况,如上设出f[i][j],则状态转移方程为:

      f[i][j]=∑k<jk=max(j-m,1)f[i-1][k]

    但是和上面不同,此时的N,M是103级别,不能像上面一样转移,用前缀和优化可以解决。

    下面就是总方案数的求法。

    在D天里,有可能有N/(M-1)~N天当中取到物品,所以总方案数为:

      ans=∑i<=ni=1f[i][n]*C(D,i)

    我们发现大组合数不好求,于是打算采用消项法,D!和(D-i)!可以消走,剩下约N项可O(N)求出,分母的i!可以求逆元解决。

    值得注意的是,相乘时因数较大,需要需要现将因数取模再相乘,否则会乘暴long long,得到光荣的WA30,只能说数据太强了。

    单次复杂度O(NM)。

 Code:

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const LL mod=998244353;
 7 const LL N=2010;
 8 int n,m;
 9 LL d,dp[N][N],f[N],jc[N],inv[N];
10 LL qpow(LL x,LL y)
11 {
12     LL ans=1;
13     while(y>0){
14         if(y&1)    ans=ans*x%mod;
15         x=x*x%mod;
16         y>>=1;
17     }
18     return ans;
19 }
20 void pre()
21 {
22     jc[0]=inv[0]=jc[1]=inv[1]=1;
23     for(int i=2;i<=2000;i++){
24         jc[i]=jc[i-1]*(LL)(i)%mod;
25         inv[i]=qpow(jc[i],mod-2);
26     }
27 }
28 int main()
29 {
30     pre();
31     while(1){
32         scanf("%d%lld%d",&n,&d,&m);
33         if(n==0&&d==0&&m==0)    break;
34         memset(dp,0,sizeof(dp));
35         dp[0][0]=1;
36         for(int i=1;i<=n;i++){
37             f[0]=dp[i-1][0];
38             for(int j=1;j<=n;j++)    f[j]=(f[j-1]+dp[i-1][j])%mod;
39             for(int j=1;j<=n;j++){
40                 dp[i][j]=f[j-1];
41                 if(j-m>=0)    dp[i][j]-=f[j-m];
42                 dp[i][j]=(dp[i][j]%mod+mod)%mod;
43             }
44         }
45         LL ans=0;
46         for(int i=1;i<=n;i++){
47             LL now=dp[i][n];
48             for(LL j=d-(LL)(i-1);j<=d;j++)    now=j%mod*now%mod;
49             now=now*inv[i]%mod;
50             ans=(ans+now)%mod;
51         }
52         printf("%lld\n",ans);
53     }    
54     return 0;
55 }
View Code

 帅哥美女们能否顺手点个推荐。

posted @ 2019-07-20 21:02  hz_Rockstar  阅读(196)  评论(0编辑  收藏  举报