题意
问某个区间内与7无关的数的平方和%1e9+7的结果是多少。
与7无关即:不含7,可以被7整除,所有位加起来可以被7整除
题解
这道题不是一般的数位DP题,一般的数位DP题都是统计某个区间内有多少个这样的数。这种数位DP就很简单了,直接DFS记忆话搜索,过滤一些情况,就可以了。
这道题过滤情况没有什么难度,难点在于要求的是平方和,以及最后极其变态的MOD要求。
因为要求平方和,所以就必须要想办法重构平方和。平方和=(i*10^(len)+x)^2。(此处将一个数拆分为两部分,进行平方和)进行分解后,可以得到2*i*10^(len)*x*cnt(cnt为能够组成这个数的可能情况)+(i*10^(len))^2+sum(x)。由上述式子可以看出,还需要两个变量,sum(x)和cnt。
cnt就很简单了,就是一个区间内符合与7无关的数的个数。
至于sum(x)依然是分解成两部分进行求解,sum(x)=(i*10^(len)+x)。后面的x就是转移到这个状态之前的sum(x),这样的话sum(x)很容就能求得。
注意事项
这道题的难点一方面在于信息的记录与转移,另一方面就在于变态的数据范围。由于数据范围很大,随时可能超LongLong,因此需要及时MOD。
代码
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 100010
#define EPS 1e-10
#define MOD 1000000007
#define int LL
using namespace std;
struct Node{
int cnt,sum,sqsum;
Node(){};
Node(int c,int s,int q):cnt(c),sum(s),sqsum(q){}
};
int dig[20];
Node dp[20][10][10];
int c[20];
Node dfs(int len,int sum,int mod,bool up){
if(len==0){
if(mod!=0&&sum!=0)
return Node(1,0,0);
else
return Node(0,0,0);
}
if(!up&&dp[len][sum][mod].cnt!=-1)
return dp[len][sum][mod];
int n=up?dig[len]:9;
Node ans=Node(0,0,0);
UP(i,0,n+1){
if(i!=7){
Node nd=dfs(len-1,(sum+i)%7,(mod*10+i)%7,up&&(i==n));
ans.cnt=(ans.cnt+nd.cnt)%MOD;
ans.sum=(ans.sum+nd.sum+((c[len-1]*nd.cnt)%MOD*i))%MOD;
ans.sqsum=(ans.sqsum+nd.sqsum+((2*nd.sum*i)%MOD*c[len-1])%MOD+(((nd.cnt*i)%MOD*c[len-1])%MOD*i*c[len-1])%MOD)%MOD;
}
}
if(!up){
dp[len][sum][mod]=ans;
}
return ans;
}
Node solve(int x){
int len=0;
W(x>0){
dig[++len]=x%10;
x/=10;
}
return dfs(len,0,0,true);
}
void init(){
c[0]=1;
UP(i,1,20){
c[i]=(c[i-1]*10)%MOD;
}
}
main(){
int kase;
scanf("%I64d",&kase);
init();
MEM(dp,-1);
W(kase--){
int a,b;
scanf("%I64d%I64d",&a,&b);
printf("%I64d\n",(solve(b).sqsum+MOD-solve(a-1).sqsum)%MOD);
}
}