hiho_1033交错和
数位动态规划
数位动态规划是求解一个大区间[L, R]中间满足条件Q的所有数字的个数(或者和,或其他)的一种方法。它通过分析每一位上的数字,一般用 dp[len][digit][...] 来表示状态“len位长的数字,最高位数字为digit所具有的xx特性”,利用记忆化搜索保存中间结果,从而加快求解速度。
通过求 f(n) 从0到n中满足条件Q的数字的个数,则所求的结果为 f(R) - f(L-1).
题目大意
给定区间[L, R],求出区间内满足条件Q的所有数字的和。条件Q:
给定数字n和一个值k,数字n的交错和定义为,若数字n的十进制表示为 a0,a1,a2...an,则交错和为 a0-a1+a2-a3... 若数字n的交错和等于k,则数字n满足条件Q。
题目分析
构造dp数组, dp[len][digit][k] 长度为len,最高位为digit,且所有数位上的数字和为k的count和sum
其中,由于每位上的数字正负号交替,因此在保存时,如果数位dig为非负,则digit = dig,否则 digit = dig+10
len(最大18)位长,最高位为digit的数字,所有数位上的数字(带正负号)和可以为(-100, 100)之间。为了用数组表示,则将和加上100,于是有了dp数组 dp[21][20][200]
实现
#pragma once #pragma execution_character_set("utf-8") // 本文件为utf-8 编码格式 #include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef long long ll; const int mod = 1000000007; struct Node{ ll count; ll sum; }; Node dp[21][20][200]; //dp数组, dp[len][digit][k] 长度为len,最高位为digit,且所有数位上的数字和为k的count和sum //其中,由于每位上的数字正负号交替,因此在保存时,如果数位dig为非负,则digit = dig,否则 digit = dig+10 //len(最大18)位长,最高位为digit的数字,所有数位上的数字(带正负号)和可以为(-100, 100)之间。为了用数组表示,则将和加上100,于是有了 //dp数组 dp[21][20][200] int bits[21]; //用于存放各个位上的数字在比该位高的位上都和最大值数的对应位上数字相同的取值范围 ll base[21]; //len数位长度,digit是首位的数字,begin_zero 表示之前是否已经开始变号,end_flag表示下一位枚举时是否枚举到 //bit[len-2]否则就枚举到9,sum为要求的数字和 Node Dfs(int len, int digit, bool begin_zero, bool end_flag, int sum){ Node t; t.sum = t.count = 0; //超出范围 if (len <= 0 || len >= 20 || digit < 0 || digit > 9 || sum < -100 || sum >= 100){ return t; } //已经存在dp的结果 if (!end_flag && dp[len][digit + (begin_zero ? 0 : 10)][sum + 100].count != -1){ return dp[len][digit + (begin_zero ? 0 : 10)][sum + 100]; } //长度只有一位 if (len == 1){ if (digit != sum) return t; t.count = 1; t.sum = sum; return t; } //开始枚举下一位数字 int end = end_flag ? bits[len - 2] : 9; int newsum = digit - sum; //最高位减去 sum, 等于次高位开始 正-负-正-负 交替... Node tmp; for (int i = 0; i <= end; i++){ //为了处理 第一位数字无效(自己加的0,),以及连续的前导0 if (!begin_zero){ tmp = Dfs(len - 1, i, i != 0, end_flag && (i == end), sum); } else{ tmp = Dfs(len - 1, i, true, end_flag && (i == end), newsum); } t.count += tmp.count; t.sum = ((t.sum + tmp.sum) % mod + ((tmp.count*digit) % mod*base[len - 1]) % mod) % mod; } if (!end_flag) dp[len][digit + (begin_zero ? 0 : 10)][sum + 100] = t; return t; } ll Solve(ll n, ll s){ if (n <= 0){ return 0; } for (int i = 0; i < 21; i++){ bits[i] = 0; } int i = 0; while (n){ bits[i++] = n % 10; n /= 10; } return Dfs(i + 1, 0, false, true, s).sum; } int main(){ ll L, R, K; memset(dp, -1, sizeof(dp)); base[0] = 1; for (int i = 1; i < 21; i++){ base[i] = (base[i - 1] * 10) % mod; } scanf("%lld %lld %lld", &L, &R, &K); ll ret1 = Solve(R, K); ll ret2 = Solve(L - 1, K); ll ret = (ret1 - ret2 + mod) % mod; printf("%lld\n", ret); return 0; }