• 单身三连之一


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

    题目大意:

      有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
    ",ans);
    53     }    
    54     return 0;
    55 }
    View Code

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

  • 相关阅读:
    移动端屏幕旋转的事件和样式方案。
    active:移动端触摸按钮的效果。
    移动端字体单位该使用px还是rem?
    Cordova/Ionic Android 开发环境搭建
    JavaScript 深拷贝(deep copy)和浅拷贝(shallow copy)
    你不知道的JS之 this 和对象原型(一)this 是什么
    你不知道的JS之作用域和闭包 附录
    你不知道的JS之作用域和闭包(五)作用域闭包
    你不知道的JS之作用域和闭包(四)(声明)提升
    你不知道的JS之作用域和闭包(三)函数 vs. 块级作用域
  • 原文地址:https://www.cnblogs.com/hz-Rockstar/p/11219240.html
Copyright © 2020-2023  润新知