codeforces712D 2100分dp

题目传送门

题意:

两个人 M\;,\;L ,初始分数分别是 a\;,\;b 。

有 t 轮比赛,每场比赛每个人获得的分数是 [-k\;,\;k] 内的随机整数。

问 t 轮比赛结束后 M 的分数大于 L 的分数的方案数。

方案的意思是 t 轮比赛每个人的得分。

两个方案不同的依据是 至少有一场比赛两人得分不同。

数据范围: 1\leqslant a\;,\;b\leqslant 100 \;,\; 1 \leqslant b \leqslant 1000 \;,\; 1 \leqslant t \leqslant 100 。

题解:

看题之后首先想了一下会不会有组合数做法。

然后又看了看范围。不管有没有组合数做法,考的就是 dp 了。

 t 轮比赛增长的分数的区间是 [-t*k\;,\; t*k]  , 状态没法存负数,我们把它变成 [0 \;,\; 2 * t*k] 。

dp[i][j] 表示前 i 轮比赛得分是 j 的方案数。

转移方程: dp[i][j] = \sum_{p=j-k}^{j+k}dp[i-1][p] 。

需要 O(1) 转移,所以我们尺取维护一下就好。注意不能越界。

最后统计答案时维护一个前缀和: sum[i] = \sum_{j = 0}^{i} dp[t][i] 。

在 t 轮比赛后 M\;,\;L 获得的分数分别是 i\;,\;j ,那么合法条件是 a + i > b + j ,

整理得 j \leqslant i - (b - a) - 1 。

我们枚举 i ,寻找合法的 j 。

统计的方式是 ans = ans + dp[t][i] * sum[i-c-1]  。

注意:转移时不要越界,对减法取模时注意负数。

感受:

见过的套路。

不过因为取模问题wa了几发。

我好菜啊。

代码:

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll ;
const int maxn = 105 ;
const int maxm = 2e5 + 5 ;
const ll mod = 1e9 + 7 ;
int a , b , k , t ;
ll f[maxn][maxm] ;
ll sum[maxm] ;
void init()
{
	int up = 2e5 ;
	f[0][100000] = 1 ;
	for(int i = 1 ; i <= t ; i ++)
	{
	   int l = 0 , r = 0 ;
	   ll sum = f[i - 1][0] ;
	   for(int j = 0 ; j <= up ; j ++)
	   {
	  	   while(r < min(up , j + k))  
			 r ++ , sum += f[i - 1][r] , sum %= mod ;
	  	   while(l < j - k)  
			 sum -= f[i - 1][l] , l ++ , sum %= mod ;
	  	   f[i][j] = (sum + mod) % mod ;
	   }	
	}
}
void solve()
{
	int up = 2e5 ;
	int c = b - a ;
	ll ans = 0 ;
	sum[0] = f[t][0] ;
	for(int i = 1 ; i <= up ; i ++)  
	  sum[i] = sum[i - 1] + f[t][i] , sum[i] %= mod ;
	for(int i = 0 ; i <= up ; i ++)
	{
		int last = min(up , i - c - 1) ;
		if(last < 0)  continue ;
		else  ans += f[t][i] * sum[last] % mod , ans %= mod ;
	}
	printf("%lld\n" , ans) ;
}
int main()
{
	scanf("%d%d%d%d" , &a , &b , &k , &t) ;
	init() ;
	solve() ;
	return 0 ;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
区间DP是一种动态规划的方法,用于解决区间范围内的问题。在Codeforces竞赛中,区间DP经常被用于解决一些复杂的字符串或序列相关的问题。 在区间DP中,dp[i][j]表示第一个序列前i个元素和第二个序列前j个元素的最优解。具体的转移方程会根据具体的问题而变化,但是通常会涉及到比较两个序列的元素是否相等,然后根据不同的情况进行状态转移。 对于区间长度为1的情况,可以先进行初始化,然后再通过枚举区间长度和区间左端点,计算出dp[i][j]的值。 以下是一个示例代码,展示了如何使用区间DP来解决一个字符串匹配的问题: #include <cstdio> #include <cstring> #include <string> #include <iostream> #include <algorithm> using namespace std; const int maxn=510; const int inf=0x3f3f3f3f; int n,dp[maxn][maxn]; char s[maxn]; int main() { scanf("%d", &n); scanf("%s", s + 1); for(int i = 1; i <= n; i++) dp[i][i] = 1; for(int i = 1; i <= n; i++) { if(s[i] == s[i - 1]) dp[i][i - 1] = 1; else dp[i][i - 1] = 2; } for(int len = 3; len <= n; len++) { int r; for(int l = 1; l + len - 1 <= n; l++) { r = l + len - 1; dp[l][r] = inf; if(s[l] == s[r]) dp[l][r] = min(dp[l + 1][r], dp[l][r - 1]); else { for(int k = l; k <= r; k++) { dp[l][r] = min(dp[l][r], dp[l][k] + dp[k + 1][r]); } } } } printf("%d\n", dp[n]); return 0; } 希望这个例子能帮助你理解区间DP的基本思想和应用方法。如果你还有其他问题,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值