吉哥系列故事——恨7不成妻

 HDU - 4507 

 

  单身! 
  依然单身! 
  吉哥依然单身! 
  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


自己的一些补充:

与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;
}

记得注意取模的地方