这个题目背景真的是让我想起了当年。。。
不说了,言归正传,这题,一眼看去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\n",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\n",ans%mod); 76 } 77 }