[Codeforces712D] Memory and Scores(DP+前缀和优化)(不用单调队列)
题面
两个人玩游戏,共进行t轮,每人每轮从[-k,k]中选出一个数字,将其加到自己的总分中。已知两人的初始得分分别为a和b,求第一个人最后获胜的方案数。两种方案被认为是不同的,当且仅当存在其中一轮,其中一人选到的数字不同。a, b, t≤100,k≤1000
分析
两个人的操作是独立的,设(dp1[i][j])表示第1个人玩i轮得到j分的方案数,第2个人同理
则有(dp1[0][a]=0)
(dp1[i][j]=sum _{u=j-k}^{j+k} dp1[i-1][u])
第2个人只需要把dp的初始值改成(dp2[0][b]=0)即可。这样的空间复杂度是(O(kt^2)),时间复杂度是(O(k^2t^2)),考虑优化。
首先可以用滚动数组,这样空间复杂度就变成了(O(kt))
注意到(u in [j-k,j+k]),可以用前缀和优化,定义(sum1[i][j] = sum_{u=-maxv}^{maxv} sum1[i][u]),则dp方程可以改写成(dp1[i][j]=sum[i-1][j+k]-sum[i-1][j+k-1]).时间复杂度(O(kt^2)),对于dp2我们同理维护
最后统计答案的时候只需要用第1个人得到i分的方案数乘上第2个人得到<i分的方案数即可,这时候又可以用上我们的sum数组
[ans=sum_{i=-maxv}^{maxv} dp1[t][i] imes sum2[t][i-1]
]
代码
#include<iostream>
#include<cstdio>
#define maxv 200100
#define mod 1000000007
using namespace std;
int a,b,k,t;
struct arr{
long long a[maxv*4+5];
inline long long& operator [] (const int index){
return a[index+maxv*2];
}
};
arr dp1[2],dp2[2];
arr sum1[2],sum2[2];
int main(){
int now1,now2;
scanf("%d %d %d %d",&a,&b,&k,&t);
dp1[0][a]=1;
for(int j=-maxv;j<=maxv;j++) sum1[0][j]=(sum1[0][j-1]+dp1[0][j])%mod;
now1=0;
for(int i=1;i<=t;i++){
now1^=1;
for(int j=-maxv;j<=maxv;j++){
dp1[now1][j]=sum1[now1^1][j+k]-sum1[now1^1][j-k-1];
dp1[now1][j]=(dp1[now1][j]+mod)%mod;
}
sum1[now1][-maxv-1]=0;
for(int j=-maxv;j<=maxv;j++){
sum1[now1][j]=sum1[now1][j-1]+dp1[now1][j];
sum1[now1][j]%=mod;
}
}
dp2[0][b]=1;
for(int j=-maxv;j<=maxv;j++) sum2[0][j]=(sum2[0][j-1]+dp2[0][j])%mod;
now2=0;
for(int i=1;i<=t;i++){
now2^=1;
for(int j=-maxv;j<=maxv;j++){
dp2[now2][j]=sum2[now2^1][j+k]-sum2[now2^1][j-k-1];
dp2[now2][j]=(dp2[now2][j]+mod)%mod;
}
sum2[now2][-maxv-1]=0;
for(int j=-maxv;j<=maxv;j++){
sum2[now2][j]=sum2[now2][j-1]+dp2[now2][j];
sum2[now2][j]%=mod;
}
}
long long ans=0;
for(int i=-k*t+a;i<=k*t+a;i++){
ans+=dp1[now1][i]*sum2[now2][i-1]%mod;
ans%=mod;
}
printf("%I64d
",ans);
}