吉哥系列故事——恨7不成妻
Time Limit: 1000/500 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 4851 Accepted Submission(s): 1590
Problem Description
单身!
依然单身!
吉哥依然单身!
DS级码农吉哥依然单身!
所以,他生平最恨情人节,不管是214还是77,他都讨厌!
吉哥观察了214和77这两个数,发现:
2+1+4=7
7+7=7*2
77=7*11
最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!
什么样的数和7有关呢?
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
Input
输入数据的第一行是case数T(1 <= T <= 50),然后接下来的T行表示T个case;每个case在一行内包含两个正整数L, R(1 <= L <= R <= 10^18)。
Output
请计算[L,R]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。
Sample Input
3
1 9
10 11
17 17
Sample Output
236
221
0
Source
2013腾讯编程马拉松初赛第一场(3月21日)
题解
这道题真的是数位Dp好题, 并不是好在他的思路有多么巧妙, 因为实际上这道题没有很高深的思路. 而是好在它很好的较全面的考察了数位Dp的能力以及对其的理解.
首先3个条件很好搞. 这也算复习了数位Dp的经典套路.
对于:
1、整数中某一位是7
方法: 你不for7就可以了.
2、整数的每一位加起来的和是7的整数倍
方法: 设一维专门维护当前维数位和mod7的余数是多少, 统计为0的即可, 这个在边界就可以处理.
3、这个整数是7的整数倍
方法: 同一上, 开一维专门维护数的模数, 这个只要模拟就可以了, 详见代码.
主要是求的不是数量而是和, 还是平方和.
对于当前这一位枚举到i的时候, 实际上你只需要递归处理好i-1之后, 再考虑加上i这一位, 那么在第i位满足条件的数, 只需要在递归处理好i-1后在前面添一个当前枚举的即可. 那么设当前枚举的是i, 在第pos位, 那么当前添了之后就是在原来的基础上加了 i * 10^pos这么多, 设这个为a. 那么新组成的数就是a + b. b为递归处理之后i-1满足的数字. 那么平方和就是(a + b)^2 = a^2 + b^2 + 2 * a * b. 那我们只需要维护i-1位的满足条件数的平方和 以及 i-1位满足条件的有多少个, i-1满足条件的数之和这三个就可以既维护好第i位的平方和的信息了.
具体转移详见代码. 由于有三个信息, 所以用结构体来存, 返回的也是结构体.
注意模意义下r算出来的的比l-1小, 减出来是负数, 所以要+mod再%mod. 这些都是对模数负数的一般处理.
#include<stdio.h>
#include<cstring>
typedef long long dnt;
const dnt p = 1e9 + 7;
int T, bit[22], len;
dnt pw[22];
struct point{
dnt su, sq, cn;
point() {cn = -1; su = sq =0;}
point(dnt su, dnt sq, dnt cn): su(su), sq(sq), cn(cn){}
}dp[22][22][10];
point dfs(int pos, dnt mod, dnt sum, bool lim){
if(!pos) return (mod && sum) ? point(0, 0, 1) : point(0, 0, 0);
if(~dp[pos][mod][sum].cn && !lim) return dp[pos][mod][sum];
point ans, now;
ans.cn = 0;
int ban = (lim) ? bit[pos] : 9;
for(int i = 0; i <= ban; ++i){
if(i == 7) continue;
dnt nsum = (sum + i) % 7;
dnt nmod = (mod * 10 + i) % 7;
now = dfs(pos - 1, nmod, nsum, lim && i == ban);
dnt a = i * pw[pos] % p, aa = a * a % p;
ans.cn = (ans.cn + now.cn) % p;
ans.su = (ans.su + now.cn * a % p + now.su) % p;
ans.sq = (ans.sq + aa * now.cn % p + 2 * a * now.su % p + now.sq) % p;
}
if(!lim) dp[pos][mod][sum] = ans;
return ans;
}
inline void resolve(dnt x){
len = 0;
while(x) bit[++len] = x % 10, x /= 10;
}
inline dnt solve(dnt x){
resolve(x);
point c = dfs(len, 0, 0, true);
return c.sq;
}
int main(){
scanf("%d", &T);
pw[1] = 1;
for(int i = 2; i <= 20; ++i) pw[i] = pw[i - 1] * 10 % p;
while(T--){
dnt l, r;
scanf("%I64d%I64d", &l, &r);
dnt ed = solve(r);
ed -= solve(l - 1);
printf("%I64d\n", (ed % p + p) % p);
}
}

本文介绍了一个有趣的编程问题,主人公吉哥因个人情感原因讨厌与数字7相关的整数。文章详细解析了一道关于寻找特定区间内与7无关的数字的平方和的问题,通过数位DP的方法给出了具体的解决方案。
9285

被折叠的 条评论
为什么被折叠?



