Problem D
题意:两人初始分数为(a,b) 总共t轮 每轮 两人随机从[-k,k]中选一个数加到自己的得分上.a,b,t<=100,k<=1000.问t轮后第一个人分数大于第2个人的方法数%mod,任意一轮中抽的不一样视为不同方案.法1:设dp[i][dif] i轮后 两人相差dif(设个offset转移负数)
dp[i][dif]+=dp[i-1][dif+(x-y)] -2k<=x-y<=2k -> dp[i][dif]+=(2k+1-z)*dp[i-1][dif+z) -2k<=z<=2k 可以向下面的方法一样化简
法2:设dp[i][sum] 抽i次 总和为sum的方法数
dp[i][sum]+=dp[i-1][sum] x=[-k,k] 时间复杂度为O(t*kt*k) TLE
化解dp[i][sum]=dp[i][sum-1]-dp[i-1][sum+k]+dp[i-1][sum-1-k] (dp[i][sum-1]拆出来多一项少一项)
时间复杂度降为O(t*tk),枚举第二个抽人的sum 则第一个人至少要抽sum+b-a+1,利用前缀求出至少抽sum的方法数即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=3e5+20;
ll a,b,k,t;
ll dp[110][N*2],pre[N*2];
ll calc(ll i,ll j)
{
if(j<0)
return 0;
return dp[i][j];
}
int main()
{
while(cin>>a>>b>>k>>t)
{
int offset=N;// offset-> zero
memset(dp,0,sizeof(dp));
dp[0][offset+0]=1;
for(int i=1;i<=t;i++)
{
for(int j=-offset;j<=offset;j++)
{
//j-k~j+k
//j-k-1~j-k-1
dp[i][j+offset]=(calc(i,j-1+offset)+calc(i-1,j+k+offset)-calc(i-1,j-k-1+offset))%mod;
dp[i][j+offset]=(dp[i][j+offset]%mod+mod)%mod;
}
}
ll ans=0;
//pre[i]
for(int i=offset;i>=-offset;i--)
pre[i+offset]=(pre[i+1+offset]+dp[t][i+offset])%mod;
for(int i=-offset;i<=offset;i++)
{
int o=i+b-a+1;
ans=(ans+(dp[t][i+offset]*pre[max(0,o+offset)])%mod)%mod;
}
cout<<ans<<endl;
}
return 0;
}