1398. 数学题3
题目描述
给定一个数字,他在十进制下从高位到低位一次是n0, n1, n2, n3,...
那么定义它的“差和”为n0-n1+n2-n3+...
如:十进制数字abcdefg,每个字母代表一个位,那么差和为a-b+c-d+e-f+g。
所以十进制数字1234567差和为1-2+3-4+5-6+7=4
现在给你们一个闭区间[m, n],请求出区间内差和为x的数字个数。
输入格式
输入只有一行,三个数字,m n x
30%: 0<= m <= n <=10^3, 50%: 0<= m <= n <=10^8, 100%: 0<= m <= n <=10^18
-100<= x <=100
输出格式
输出只有一个数字,表示区间内差和为x的数字的总和mod1000000007的结果。
Sample Input
100 111 1
Sample Output
211
这题不会做,妥妥的。为了看懂助教的代码,费了好大劲儿,干脆加点注释,防止以后有用。
要点:
1.由于区间太大,无法遍历,所以要把一个数按位处理,有点转化为数组的意思。
2.动归记录的数组中有遍历所有和差值,为了能用数组下标表示,把-100-100转化到0-200,计算一个数的和差值时加上100,这样虽然和差值变了,但对应和差值的数是没有变的。
3.需要同时更新满足和差值的所有数的和以及个数,之所以要记录个数,是为了加上最高位对应的值,比如首位为1长度为5的情况,它的数之和为所有对应满足的长度为4的数之和加上 个数*10000.
4.在最高位确定情况下,它的子问题(即位数少一位)的首位数是可以为零的。
具体看代码及注释:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #define LIMIT 1000000007 3 4 long long dp[22][10][220][2] = {}; //分别表示数字的长度,数字首位数,和差的值+100,满足和差值的所有数的和以及个数 5 long long basics[22] = {0, 1}; //预处理的数组,方便使用,分别为0,1,10,100,。。。 6 long long lower, upper, sum; //三个输入的值 7 8 long long GetResult(long long number){ 9 int len = 0; 10 for(long long i = number; i; i /= 10, ++len); 11 long long result = 0; 12 for(int i = 1; i < len; ++i) //位数小于len的情况,首位数不能为零 13 for(int j = 1; j < 10; ++j) 14 result = (result + dp[i][j][sum + 100][0]) % LIMIT; 15 long long num = 0; 16 // 下面这个比较复杂,比如3333这个数,首先对于首位小于3长度为4的情况,直接加上dp中的值(此时num为0) 17 // 但是对于3则不行,因为不包含所有子情况,所以单独处理,也就是数字为333的情况的和加上 个数*3000(此时num为3) 18 //对于333也是一样,达到3的时候处理33加上(3300 *个数) 19 for(int i = len, tmp_sum = sum + 100, j; i > 0; 20 number -= basics[i] * j, --i, tmp_sum = 200 - (tmp_sum - j), num = num * 10 + j){ 21 for(j = 0; basics[i] * (j+1) <= number; ++j){ 22 if (i == len && j == 0) //排除长度为len时首位为0,长度小于len首位可以为0 23 continue; 24 result += dp[i][j][tmp_sum][0] + dp[i][j][tmp_sum][1] * (num * basics[i+1] % LIMIT) % LIMIT; 25 } 26 result %= LIMIT; 27 } 28 return result; 29 } 30 31 int main(){ 32 for(int i = 2; i < 22; ++i) 33 basics[i] = basics[i-1] * 10; 34 scanf("%lld%lld%lld", &lower, &upper, &sum); 35 for(int i = 0; i < 10; ++i){ // 长度为一时的处理 36 dp[1][i][i+100][0] = i; 37 dp[1][i][i+100][1] = 1; 38 } 39 for(int i = 2; i < 22; ++i) 40 for(int j = 0; j < 10; ++j) 41 for(int k = 0; k < 200; ++k){ 42 for(int m = 0; m < 10; ++m){ 43 dp[i][j][k][0] += dp[i-1][m][200 - (k - j)][0]; //首位数为j,和差为k对应的长度为i-1的和差为200-(k-j) 44 dp[i][j][k][1] += dp[i-1][m][200 - (k - j)][1]; //遍历所有子问题并更新 45 } 46 dp[i][j][k][0] += dp[i][j][k][1] * (j * basics[i] % LIMIT) % LIMIT; //加上最高位的对应值 47 dp[i][j][k][0] %= LIMIT; 48 dp[i][j][k][1] %= LIMIT; 49 } 50 long long result_upper = GetResult(upper+1); 51 long long result_lower = GetResult(lower); 52 long long result = result_upper - result_lower; 53 if(result < 0) 54 result += LIMIT; 55 printf("%lld ", result); 56 }