7.19T1
现在的我是T2一点头绪都搞不到(然而大家都快要A掉它了,我还处在M0的状态),T3只能摸到个大体思路,但是还是模模糊糊的,决定写点题解,冷静一下
通过T1部分分式子的过程,让我意识到对于一道明确的DP题,一定要在算草纸上写一写,推一推,你可以列出来你有什么,你需要什么,然后去依据DP的实际意义进行拼凑,当然对自己DP式子的不断优化也是需要的
30分其实很好拿,我们设定$dp[i][j]$代表到第$i$天还剩下$j$块饼干没有被分出去,那么这个$i$应该属于$[1,d]$,那很容易可以想到$dp[i][j]+={\sum}dp[i-1][k]$,$k$属于$[j,j+m-1]$,那应该在优化时可以想到这个$\sum$可以用前缀和搞定,关于初始化令$dp[0][n]=1$然后处理一下前缀和就可以了
但实际上我们很容易就能够看出这个想法的不足之处,比如你的第一维要循环$d$,不论时间会不会炸,空间一定是炸了,并且是根本开不到的那种,但是你会发现$n$其实算的上很小,而且对于$d$远大于$n$的情况,一定是有很多天都没有给饼干,那我们完全就可以把这些什么都没有给出的天从$dp$中除名,这样的话空间,时间复杂度都可以降下来很多,那我们就重新来定义$dp[i][j]$的含义,我们用$dp[i][j]$来表示,已经给出去饼干$i$天(给出去饼干就代表饼干数不为0),一共给出去$j$块饼干,我们模仿着刚才的思路,依旧可以写出来一个前缀和优化过的DP式子,$dp[i][j]=sum[i-1][j]-sum[i-1][j-m]$,其实这个东西是在可想的范围内的,但是我没想啊,那能有什么办法,怪自己呗
关于这个$sum[i-1][j-m]$一定要注意一下$j-m$是不是小于0了,最好还是不要下标越界,关于取模,只要你对自己的时间复杂度足够自信,那你就随便模,如果有做减法的情况,一定要先加一个模数,免得出现负数,在这里在给一个小建议,如果不知道自己会不会得到什么奇奇怪怪的负数,那就自己去造几组比较大的数据,没有暴力的话,没办法看对错,但是如果出现负数了,证明你肯定不对
最后的$ans={\sum}dp[i][n]{\times}C_d^i$,在我们对$C$进行计算的时候会发现不论是$Lucas$还是杨辉三角打表,下标为$d$都不是可用的好方法,那我们思考一下$C_n^m=\frac{n!}{m!{\times}(n-m)!}$,我们进行一下简单的消项,那$C_n^m=\frac{n{\times}(n-1){\times}{\cdots}{\times}(n-m+1)}{m!}$,那对于分母上m!我们可以求一下逆元,对于分子那一大坨,因为$i$每次之加一,完全可以递推$O(1)$实现,这样的话我们不仅解决了这道题,还得到了一个求C的新思路
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define ll long long 5 #define maxn 2010 6 using namespace std; 7 const long long mod=998244353; 8 int n,m; 9 ll d,ans; 10 ll jc[maxn],ny[maxn]; 11 ll dp[maxn][maxn],sum[maxn][maxn]; 12 ll ksm(ll a,ll b) 13 { 14 ll ans=1; a=a%mod; 15 while(b>0) 16 { 17 if(b&1) ans=(ans*a)%mod; 18 b=b>>1; a=(a*a)%mod; 19 } 20 return ans%mod; 21 } 22 void clear() 23 { 24 memset(dp,0,sizeof(dp)); memset(sum,0,sizeof(sum)); 25 memset(jc,0,sizeof(jc)); memset(ny,0,sizeof(ny)); 26 ans=0; dp[0][0]=1; jc[0]=1; 27 for(int i=0;i<=n;++i) sum[0][i]=1; 28 for(int i=1;i<=n;++i) jc[i]=(jc[i-1]*i)%mod; 29 ny[n]=ksm(jc[n],mod-2); 30 for(int i=n;i>=1;--i) ny[i-1]=(ny[i]*i)%mod; 31 } 32 int main() 33 { 34 while(1) 35 { 36 scanf("%d%lld%d",&n,&d,&m); 37 if(n==0&&d==0&&m==0) break; 38 if(d*(ll)m<(ll)n) {printf("0\n"); continue;} 39 clear(); 40 for(int i=1;i<=min((ll)n,d);++i) 41 { 42 for(int j=i;j<=n;++j) 43 { 44 if(j-m<0) 45 { 46 // cout<<"j-m="<<j-m<<endl; 47 // cout<<"sum["<<i-1<<"]["<<j-1<<"]="<<sum[i-1][j-1]<<" "; 48 dp[i][j]=sum[i-1][j-1]; 49 // cout<<"dp["<<i<<"]["<<j<<"]=sum["<<i-1<<"]["<<j-1<<"]"<<" "; 50 // if(dp[i][j]<0) cout<<"dp["<<i<<"]["<<j<<"]="<<dp[i][j]<<endl; 51 } 52 else 53 { 54 // cout<<"sum["<<i-1<<"]["<<j-1<<"]="<<sum[i-1][j-1]<<" "; 55 // cout<<"sum["<<i-1<<"]["<<j-m<<"]="<<sum[i-1][j-m]<<" "; 56 dp[i][j]=(sum[i-1][j-1]-sum[i-1][j-m]+mod)%mod; 57 // cout<<"dp["<<i<<"]["<<j<<"]=sum["<<i-1<<"]["<<j-1<<"]-"; 58 // cout<<"sum["<<i-1<<"]["<<j-m<<"]"<<" "; 59 // if(dp[i][j]<0) cout<<"dp["<<i<<"]["<<j<<"]="<<dp[i][j]<<endl; 60 } 61 sum[i][j]=(sum[i][j-1]+dp[i][j])%mod; 62 // if(sum[i][j]<0) cout<<"sum["<<i<<"]["<<j<<"]="<<sum[i][j]<<endl; 63 } 64 } 65 ll ls=d%mod; 66 for(int i=1;i<=min((ll)n,d);++i) 67 { 68 ll zhs=ny[i]; 69 if(i!=1) ls=(ls*(d%mod-(ll)i+(ll)1)%mod)%mod; 70 zhs=(zhs*ls)%mod; 71 // for(int j=d-i+1;j<=d;++j) zhs=(zhs*j)%mod; 72 // cout<<"C("<<d<<","<<i<<")="<<zhs<<endl; 73 ans=(ans+(dp[i][n]*zhs)%mod)%mod; 74 // if(ls<0) cout<<ls<<endl; 75 } 76 // for(int i=1;i<=min((ll)n,d);++i) 77 // for(int j=0;j<=n;++j) cout<<"dp["<<i<<"]["<<j<<"]="<<dp[i][j]<<endl; 78 // for(int i=1;i<=((ll)n,d);++i) 79 // for(int j=0;j<=n;++j) cout<<"sum["<<i<<"]["<<j<<"]="<<sum[i][j]<<endl; 80 // while(ans<0) ans=ans+mod; 81 printf("%lld\n",ans%mod); 82 } 83 return 0; 84 }