• HZOJ 20190719 那一天我们许下约定(dp+组合数)


    这个题目背景真的是让我想起了当年。。。

    不说了,言归正传,这题,一眼看去30分暴力还是很好拿的,但我因为考试时的心态问题没有处理好细节爆了零。

    30分暴力的普遍思路的复杂度应该是$O(nmd)$的,但是d的数据范围实在恐怖,根本无法AC。

    30分思路看上去优化空间不是很大(但是有神犇想出了矩阵快速幂加速转移,TQL)。

    我们考虑转换一下思路,首先d肯定是不能放进复杂度里的,那又要怎么转移呢,我们观察到因为最多有n块饼干,所以最多真正给她饼干的天数也就是n天,那么我们根据这个来设计状态:设$f[i][j]$表示真正给她$i$天,给了$j$块饼干的方案总数。那么我们可以的出状态转移方程即为$f[i][j]=Sigma_{k=j-m}^{j-1}{f[i-1][k]}$,

    $ans=Sigma{f[i][n]*C_d^i}$。还是很好理解的趴,但是我们现在的dp式子的复杂度依然是$O(n^3)$的,考虑优化,后面的式子是一个区间和的形式,这样就很容易想到前缀和优化。复杂度就变成了$O(n^2)$。

    还有就是这题要注意的几点:首先是d太大无法用我们平常用的方式求出,最好是将组合数公式展开约分求解。

                 其次就是在第二层循环里,本来应该是枚举$j$从$i$到$i*(m-1)$,但是前缀和要处理到$n$,因为你的前缀和是要为下一层服务的,而下一层很可能要用到$n$,所以前缀和一定要处理到$n$。

    优化dp的题之前还是做的不多,这题也算练练吧。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<queue>
     6 #include<vector>
     7 #define int long long
     8 #define ll long long
     9 using namespace std;
    10 const int N=2005;
    11 const int mod=998244353;
    12 int dp[N][N],sum[N][N],inv[N];
    13 int qpow(int a,int b){
    14     int ans=1;
    15     a%=mod;
    16     while(b){
    17         if(b&1) ans=ans*a%mod;
    18         b>>=1;
    19         a=a*a%mod;
    20     }
    21     return ans; 
    22 }
    23 /*inline int C(int a,int b){
    24     int js=1ll,facd=1ll;
    25     for(register int i=1;i<=b;i++) js=(js*i)%mod;
    26     for(register int i=a-b+1;i<=a;i++) {facd=i%mod*facd%mod;cout<<"L"<<facd<<endl;}
    27     return facd*qpow(js,mod-2)%mod; 
    28 }*/
    29 /*inline int C(int x,int y){
    30      int facd=1ll,faci=1ll;
    31      x%=mod;
    32      for(register int i=2;i<=x;++i)     faci=(faci*i)%mod;
    33      for(register int i=y-x+1;i<=y;++i) facd=i%mod*facd%mod;
    34      int inv=qpow(faci,mod-2);
    35      //printf("%lld %lld %lld
    ",faci,facd,inv);
    36      return facd*inv%mod;
    37 }*/
    38 int C(int y,int x){
    39     if(y<0ll||x<0ll||y<x)return 0;
    40     y%=mod;
    41     if(y==0ll||x==0ll)return 1;
    42     ll ans=1;
    43     for(int i=0ll;i<x;i++)
    44         ans=ans*(y-i)%mod;
    45     for(int i=1ll;i<=x;i++)
    46         ans=ans*inv[i]%mod;
    47     return ans;
    48 }
    49 signed main(){
    50     int n,m,d;
    51     for(int i=1ll;i<=2000ll;i++)
    52         inv[i]=qpow(i,mod-2ll);
    53     while(~scanf("%lld%lld%lld",&n,&d,&m)){
    54         if(!n&&!m&&!d) break;
    55         if(n>d*(m-1)) {puts("0");continue;}
    56         memset(dp,0,sizeof(dp));
    57         memset(sum,0,sizeof(sum));
    58         for(int i=1;i<m;i++){
    59             dp[1][i]=1;
    60             sum[1][i]=dp[1][i]+sum[1][i-1];
    61         }
    62         for(int i=m;i<=n;i++) sum[1][i]=sum[1][i-1];
    63         int ans=0;
    64         ans+=(dp[1][n]*C(d,1ll))%mod;
    65         for(int i=2;i<=min(d,n);i++){
    66             for(int j=i;j<=n;j++){
    67                 dp[i][j]=((sum[i-1][j-1]-sum[i-1][max(j-m,0ll)])%mod+mod)%mod;
    68                 sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
    69                 //cout<<dp[i][j]<<endl;
    70             }
    71             ans=(ans+dp[i][n]*C(d,i)%mod)%mod;
    72         }
    73         //cout<<dp[1][n]<<endl;
    74         //for(int i=2;i<=min(d,n);i++) ans=(ans+dp[i][n]*C(d,i)%mod)%mod;
    75         printf("%lld
    ",ans%mod);
    76     }
    77 }
    View Code
  • 相关阅读:
    Oracle select for update and for update nowait
    Oracle DML , DDL , DCL
    Shell变量的作用域:Shell全局变量、环境变量和局部变量
    Shell脚本的调试方法
    openlayers6地图全图以及框选截图导出功能(附源码下载)
    Markdown基本语法
    sql 四大排名函数---(ROW_NUMBER、RANK、DENSE_RANK、NTILE)简介
    Newtonsoft.Json 去掉
    辽宁软考报名地址
    burp suite professional安装及使用教程
  • 原文地址:https://www.cnblogs.com/leom10/p/11221286.html
Copyright © 2020-2023  润新知