Memory and his friend Lexa are competing to get higher score in one popular computer game. Memory starts with score a and Lexa starts with score b. In a single turn, both Memory and Lexa get some integer in the range [ - k;k] (i.e. one integer among - k, - k + 1, - k + 2, ..., - 2, - 1, 0, 1, 2, ..., k - 1, k) and add them to their current scores. The game has exactly t turns. Memory and Lexa, however, are not good at this game, so they both always get a random integer at their turn.
Memory wonders how many possible games exist such that he ends with a strictly higher score than Lexa. Two games are considered to be different if in at least one turn at least one player gets different score. There are (2k + 1)2t games in total. Since the answer can be very large, you should print it modulo 109 + 7. Please solve this problem for Memory.
The first and only line of input contains the four integers a, b, k, and t (1 ≤ a, b ≤ 100, 1 ≤ k ≤ 1000, 1 ≤ t ≤ 100) — the amount Memory and Lexa start with, the number k, and the number of turns respectively.
Print the number of possible games satisfying the conditions modulo 1 000 000 007 (109 + 7) in one line.
解题思路:
dp[i][j] 表示通过前 i 轮总计得分为 j 的情况 但是直接看上去可能会是一个三重循环 复杂度爆炸
这道题主要用到了dp转移的一个优化技巧,利用维护前一行的前缀和,来压缩一维变量的目的(不用枚举当前轮得分情况)。
思路具体的说明我认为这篇文章已经写的非常清楚了。链接
会有一些具体的细节已经写进注释里了。
AC代码:
/*
* @Author: wchhlbt
* @Last Modified time: 2017-10-03
*/
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define pb push_back
#define AA first
#define BB second
#define ONES(x) __builtin_popcount(x)
#define _ << " " <<
using namespace std;
typedef pair<int, int> P;
typedef long long ll ;
int dx[4] = {0,0,1,-1};
int dy[4] = {1,-1,0,0};
const double eps =1e-8;
const int mod = 1000000007;
const double PI = acos(-1.0);
inline int read(){ int num; scanf("%d",&num); return num;}
const int maxn = 200007;
ll dp[107][maxn];//前i轮得分为j的方案数
ll sum[maxn];//记录dp[i][j]的前缀和
ll tsum[maxn];//临时备份
int main()
{
int a,b,k,t;
scanf("%d%d%d%d",&a,&b,&k,&t);
int o = 1e5+1;
dp[0][o] = 1;//因为得分有可能是负数,所以整体有一个偏移
for(int i = 1; i<maxn; i++)
sum[i] = sum[i-1] + dp[0][i];
for(int i = 1; i<=t; i++){
for(int j = 1; j<maxn; j++){
int l = max(1,j-k);//记录对应区间的左边界
int r = min(200000+1,j+k);//记录对应区间的右边界
dp[i][j] = (sum[r] - sum[l-1]) % mod;
dp[i][j] = (dp[i][j] + mod) % mod;
tsum[j] = (tsum[j-1] + dp[i][j]) % mod;//记录dp[i][j]的前缀和
}
for(int j = 1; j<maxn; j++)
sum[j] = tsum[j];//拷贝进sum数组
}
ll ans = 0;
for(int i = 2e5+1; i>=1; i--){
int j = a + i - b - 1;
if(j<=0) break;
j = min(200000+1, j);//注意这里不要越界
if(dp[t][i])
ans = (ans + (sum[j] % mod) * (dp[t][i] % mod) ) % mod;
}
cout << ans << endl;
return 0;
}