【Hihocoder - 1033】Alternating Sums - 交错和

交错和


@题目描述@

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个数 x,设它十进制展从高位到低位上的数位依次是 a 0 , a 1 , . . . , a n 1 ,定义交错和函数:

f ( x ) = a 0 a 1 + a 2 . . . + ( 1 ) n 1 a n 1

例如: f ( 3214567 ) = 3 2 + 1 4 + 5 6 + 7 = 4
给定 l, r, k,求在 [l, r] 区间中,所有 f(x) = k 的 x 的和,即:
描述

输入
输入数据仅一行包含三个整数, l , r , k ( 0 l r 10 18 , | k | 100 )

输出
输出一行一个整数表示结果,考虑到答案可能很大,输出结果模 10 9 + 7

样例输入
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都求的是方案数。怎么办?
从状态转移来说,我们第 i 位的状态应该由第 i 1 位的状态转移过来。状态转移应该是这样的

i a i = i 1 + i 1 a i 10 i 1

于是我们统计总和时,不仅要用到 i 1 位满足约束的状态总和,还要用到 i 1 位满足约束的状态数量。所以定义两种状态:
d p [ i ] [ k ] i k +

d p 1 / d p 2

@状态转移-预处理@

(以下状态转移忽略坐标平移的问题)
(实现详情见代码,作者习惯采用预处理而不是记忆化搜索实现)
显然的,边界状态为:

d p 1 [ 1 ] [ i ] = i d p 2 [ 1 ] [ i ] = 1 0 <= i <= 9 )

假设现在递推到了第 i 位,枚举交错和为 k ,枚举第 i 位上的数为 j ,则交错和还剩下 k j 。但是第 i 1 位取的是 ,而我们的状态表达的是首位为 + 。所以我们需要将 i 1 位以后的数字取相反数,使得第 i 1 位取的是 + 。相应地, i 1 位以后的交错和也变为了 ( k j )
所以:
d p 1 [ i ] [ k ] = j = 0 j <= 9 ( d p 1 [ i 1 ] [ ( k j ) ] + j 10 i 1 d p 2 [ i 1 ] [ ( k j ) ] )

d p 2 [ i ] [ k ] = j = 0 j <= 9 d p 2 [ i 1 ] [ ( k j ) ]

@答案求解@

数位dp的老套路。
从高位向低位枚举第 i 位,枚举第 i 位上的数 j < d i g h t [ i ] ,算出前几位的交错和 s 。然后用 k s 算出后几位的交错和,再用对应的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

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~

猜你喜欢

转载自blog.csdn.net/tiw_air_op1721/article/details/80193776
今日推荐