codeforces 712D Memory and Scores (dp)

D. Memory and Scores

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.

Input

The first and only line of input contains the four integers abk, and t (1 ≤ a, b ≤ 1001 ≤ k ≤ 10001 ≤ t ≤ 100) — the amount Memory and Lexa start with, the number k, and the number of turns respectively.

Output

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;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值