educational codeforces round8 D 数位dp

本文介绍了一种使用数位动态规划解决数字统计问题的方法,特别是涉及模运算的问题。通过实例讲解了如何利用数位DP来计算特定范围内数字的模m余数为0的数量,并提供了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上次好像接触过一次数位DP……因为有数学在里面,所以转移起来比较麻烦

数位的DP用于数字中的统计,尤其是和模有关系的

这题DP[I][J][K]表示前i个,模m为j,是否达到上限,然后转移……要考虑不少转移时候的细节

要求区间a到b,你只要求0到a和0到b就可以了,然后相减,数位dp主要是因为题目要求的是倍数关系,或者模,所以只要保存前i位的模就可以进行状态转移

感觉还是没把细节考虑清楚

DP初始化的时候也要考虑清楚……

代码有过了一般的测试点,有几个没过。。不管

//感觉还是要想清楚之后再编程,不然bug太多了 
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int a[2005];
int b[2005];
int m,d;
LL dp[2001][2001][2];//前i位,模m等于j的种数,如果和b前i位相等,就取1 
const int M = 1000000007;
int n = 0;
LL solve(int *b)
{
    memset(dp,0,sizeof(dp));
    dp[0][0][1] = 1;
    for(int i = 0; i < n; i++)
    for(int j = 0; j < m; j++)
    for(int k = 0; k < 2; k++)
    if (i % 2 == 0){//填充奇数位 
    	if (k == 0){//随便填,不能等于d 
            for(int p = 0; p <= 9; p++)
            if (p != d)
            	dp[i + 1][(j*10 + p) % m][0] = (dp[i + 1][(j*10 + p) % m][0] + dp[i][j][k]) %M;		
		}
		else{ //填充第一位不能为0 
		    if (b[i + 1] != d && !(i == 0 && b[i + 1] == 0))dp[i + 1][(j * 10 + b[i + 1])%m][1] = (dp[i + 1][(j * 10 + b[i + 1])%m][1] + dp[i][j][k])%M;//是第一位的时候不能填0,其他位不能填d 
			for(int p = 0; p < b[i + 1]; p++)  if (p != d && !(i == 0 && p == 0)) dp[i + 1][(j * 10 + p) %m][0] = (dp[i + 1][(j * 10 + p) %m][0] + dp[i][j][k])%M;
		}
    }
    else{//填充偶数位 
    	int jj =(j * 10 + d) % m ;
		if(k == 0)
    		dp[i + 1][jj][0] = (dp[i + 1][jj][0] + dp[i][j][k])%M;//填d 
		else{
			if(b[i + 1] > d) dp[i + 1][jj][0] = (dp[i + 1][jj][0] + dp[i][j][k])%M;//变成7之后就不是b的前缀了 
			else if (b[i + 1] == d) dp[i + 1][jj][1] = (dp[i + 1][jj][1] + dp[i][j][k])%M;//还是b的前缀 
			//其他情况,不能变成d	
			}
    }
    
	return (dp[n][0][1] + dp[n][0][0]) % M;//和就是b和不是b 
}
int main()
{
	cin >> m >> d;
	getchar();
	char c;
	while((c = getchar()) != '\n')
		a[++n] = c -'0';
	n = 0;
	while((c = getchar()) != '\n')
		b[++n] = c -'0';
	//cout << solve(b);
	//cout << solve(a);
	cout << (solve(b) - solve(a) + M) %M; 
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值