吉哥系列故事——恨7不成妻
单身!
依然单身!
吉哥依然单身!
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
思路:
利用数位dp
参考https://www.cnblogs.com/neopenx/p/4008921.html
自己的一些补充:
与7不沾边的数需要满足三个条件。
①不出现7
②各位数和不是7的倍数
③这个数不是7的倍数
这三个条件都是基础的数位DP。
但是这题要统计的不是符合条件个数,而是平方和。
也就是说在DP时候,要重建每个数,算出平方,然后求和。
需要维护三个值(推荐使用结构体), 假定dfs推出返回的结构体是next,当前结果的结构体是ans
①符合条件数的个数 cnt
②符合条件数的和 sum
③符合添加数的平方和 sqsum
其中①是基础数位DP。②next.sum+(10^len*i)*next.cnt,其中(10^len*i)*next.cnt代表以len为首位的这部分数字和。
③首先重建一下这个数,(10^len*i+x),其中x是这个数的后面部分,则平方和就是(10^len*i)^2+x^2+2*10^len*i*x,首先需要注意的是x表示的是某一个数字,而不是上面说的next.sum, 两者是不对等的。
整体还要乘以next.cnt,毕竟不止一个。其中x^2*next.cnt=next.sqsum
这样sqsum+=next.sqsum
sqsum+=(2*10^len*i*x)*next.cnt=(2*10^len*i)*next.sum(神奇的化简)
sqsum+=(10^len*i)^2*next.cnt
最后可以知道
x^2 * next.cnt = next.sqsum
x*next.cnt = next.sum
其中 x 表示某一个数字的右边较小几位的值 比如234 那么x就是34 ,此时的平方和就是(34+ 2*10^2)^2 。
其实这两个等式一直是比较疑惑的地方,但是随便举几个例子确实相等。
现在理解 :x*next.cnt = next.sum
如上式子 next.sum+(10^len*i)*next.cnt
我们可以知道等式 next.sum+(10^len*i)*next.cnt =(10^len*i+x)*next.cnt
所以有 x*next.cnt = next.sum
接下来理解 :x^2 * next.cnt = next.sqsum
比如 12 , 在迭代到 i =1时,那么对应的有 10,11,12 这三个数字 平方和就是(10+0)^2+(10+1^2)^2+(10+2^2)^2
那么其中的 x 就分别表示 0、1、2 就是对应的x^2 ,最终 0^2 +1^2+2^2=5 就是等于这个数字第十位的后面部分,也就是个位
的平方和 = next.sqsum。
代码:
#include<iostream>
#include<stdio.h>
#include<cstring>
typedef long long ll;
using namespace std;
int caseT;
ll A,B;
int digit[20];
ll pow[25];
const ll mode = 1e9+7;
struct node{
// num = 对应的合法的数字的数量
// sum = 对应合法数字的加和
// sqsum = 合法数字的平方和
ll num,sum,sqsum;
node(){
num = -1;sum = sqsum = 0;
}
node(ll x,ll y,ll z):num(x),sum(y),sqsum(z){};
};
//其中表示三个状态
// pos 对应的数位
// re1 表示每一位加和%7 的结果,所以只要7个空间就够了
// val 表示这个整数是7的整数倍 %7 的结果
node dp[20][10][10]; //pos , res ,val
//其中表示三个状态
// pos 对应的数位
// re1 表示每一位加和%7 的结果,所以只要7个空间就够了
// val 表示这个整数是7的整数倍 %7 的结果
node dfs(int pos,int re1,int val,bool limit){
if(pos<=-1) return (re1!=0&&val!=0)?node(1,0,0):node(0,0,0);
if (!limit && dp[pos][re1][val].num!=-1) return dp[pos][re1][val];
int up = limit?digit[pos]:9;
node ans(0,0,0);
for(int i=0;i<=up;i++)
if(i!=7){
node next = dfs(pos-1,(re1+i)%7,(val*10+i)%7,limit && i==up);
ans.num+=next.num%mode;
ans.num%=mode;
ans.sum +=next.sum+(pow[pos]*i)%mode*next.num%mode;
ans.sum%=mode;
ans.sqsum += next.sqsum%mode;
ans.sqsum +=i*pow[pos]%mode*i*pow[pos]%mode*next.num%mode;
ans.sqsum +=2*next.sum%mode*pow[pos]%mode*i%mode;
ans.sqsum%=mode;
}
if(!limit) dp[pos][re1][val] = ans;
return ans;
}
ll solve(ll x){
int pos =0;
while(x>0){
digit[pos++] = x%10;
x/=10;
}
node res = dfs(pos-1,0,0,true);
return res.sqsum;
}
int main(){
scanf("%d",&caseT);
pow[0]=1;
for(int i=1;i<=20;i++) pow[i] = pow[i-1]*10%mode;
while(caseT--){
scanf("%lld %lld",&A,&B);
ll ans = solve(B)-solve(A-1);
printf("%lld\n",(ans%mode+mode)%mode);
}
return 0;
}
记得注意取模的地方