交错和
@题目描述@
时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个数 x,设它十进制展从高位到低位上的数位依次是
,定义交错和函数:
例如:
给定 l, r, k,求在 [l, r] 区间中,所有 f(x) = k 的 x 的和,即:
输入
输入数据仅一行包含三个整数,
。
输出
输出一行一个整数表示结果,考虑到答案可能很大,输出结果模
。
样例输入
100 121 0
样例输出
231
提示
对于样例 ,满足条件的数有 110 和 121,所以结果是 231 = 110 + 121。
更多样例:
Input
4344 3214567 3
Output
611668829
Input
404491953 1587197241 1
Output
323937411
Input
60296763086567224 193422344885593844 10
Output
608746132
Input
100 121 -1
Output
120
@解析@
一眼数位dp题。
@状态定义@
首先:k值可能为负。因此先坐标平移一下,给每一个算出来的和加上一个100。
然后开始定义状态。这时候我们发现题目所求的是满足条件数的和,但是我们常做的数位dp都求的是方案数。怎么办?
从状态转移来说,我们第
位的状态应该由第
位的状态转移过来。状态转移应该是这样的
于是我们统计总和时,不仅要用到 位满足约束的状态总和,还要用到 位满足约束的状态数量。所以定义两种状态:
@状态转移-预处理@
(以下状态转移忽略坐标平移的问题)
(实现详情见代码,作者习惯采用预处理而不是记忆化搜索实现)
显然的,边界状态为:
假设现在递推到了第 位,枚举交错和为 ,枚举第 位上的数为 ,则交错和还剩下 。但是第 位取的是 ,而我们的状态表达的是首位为 。所以我们需要将 位以后的数字取相反数,使得第 位取的是 。相应地, 位以后的交错和也变为了 。
所以:
@答案求解@
数位dp的老套路。
从高位向低位枚举第
位,枚举第
位上的数
,算出前几位的交错和
。然后用
算出后几位的交错和,再用对应的dp数组,注意一下我们上面预处理所提到的首位为
要取相反数的问题就可以了……
吗?
注意了!这里的首位如果为0,那么根据定义,那是非法的状态!
所以我们要处理首位为0的状态的问题。
@代码君@
如果有些我没能解释清楚的……
就当锻炼各位的代码阅读能力咯!
#include<cstdio>
const int MOD = int(1E9) + 7;
typedef long long ll;
ll dp1[20][200], dp2[20][200], pow[20];
void init() {
for(int i=0;i<=9;i++) {
dp1[1][100+i] = i;
dp2[1][100+i] = 1;
}
dp2[0][100] = 1;
pow[1] = 1;
for(int i=2;i<=18;i++) {
pow[i] = (pow[i-1] * 10) % MOD;
for(int j=0;j<=9;j++) {
for(int k=0;k<=200;k++) {
if( k >= j ) {
dp1[i][k] = ( dp1[i][k] + ((j*pow[i])%MOD*dp2[i-1][200-(k-j)])%MOD + dp1[i-1][200-(k-j)] ) % MOD;
dp2[i][k] = ( dp2[i][k] + dp2[i-1][200-(k-j)] ) % MOD;
}
}
}
}
}
int dight[20], cnt;
ll Get_Ans(ll n, int k) {
cnt = 0;
do
{
dight[++cnt] = n % 10;
n /= 10;
}while( n );
ll res = 0, now = 200-k, sum = 0;
for(int i=cnt;i>=1;i--) {
for(int j=0;j<dight[i];j++) {
res = (res + dp1[i-1][now] + (sum * pow[i])%MOD * dp2[i-1][now]) % MOD;
now++, sum++;
}
now = 200 - now;
sum = sum * 10 % MOD;
}
for(int i=cnt-1;i>=1;i--)
res = (res + dp1[i][k] - dp1[i][200-k] + MOD) % MOD;
return res;
}
int main() {
init();
ll l, r;int k;
scanf("%lld%lld%d", &l, &r, &k);
k += 100;
printf("%lld\n", (Get_Ans(r+1, k) - Get_Ans(l, k) + MOD) % MOD);
}
END
就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~