一般的数位dp都是输出方案数,但这道题求平方和。
所以要用一个结构体保存dfs到这一层的信息:有多少个数满足条件cnt,满足条件的数的和是多少,它们的平方和是多少(一般求平方和都要维护和)
注意:记忆化搜索时要判断lim,如果有lim,就不能记录,因为记录了下一次就会被直接返回,而有限制的位就没有被搜索到
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
int num[20];
ll pow10[22];
struct node {
ll cnt,power,sum;
node() { cnt=-1; power=0; sum=0; }
node(ll cnt,ll power,ll sum) : cnt(cnt),power(power),sum(sum) { }
}dp[20][200][10];
node dfs(int pos,int sum,int mod1,int lim)
{
if(pos==1){
if((sum%7==0)||(!mod1)) return node(0,0,0);
return node(1,0,0);
}
if(!lim&&dp[pos][sum][mod1].cnt!=-1) return dp[pos][sum][mod1];
int up=lim?num[pos-1]:9; //printf("up:%d
",up);
node rt=node(0,0,0);
for(int i=0;i<=up;i++){
if(i==7) continue;
int nx_lim=(lim&&i==up),nx_mod=(mod1*10+i)%7;
//这一位的贡献=这一位的最高位i*对应的10的次方再加上下一位
node nx=dfs(pos-1,sum+i,nx_mod,nx_lim);
rt.cnt=(rt.cnt+nx.cnt)%mod;
rt.sum=(rt.sum+(nx.sum + i * pow10[pos-1] %mod * nx.cnt %mod) %mod )%mod;
//nx.power是已经算出来后面的一堆数的平方和 现在那一堆数应该加上这一位的贡献再平方
//x^2+y^2 x是nx.power y是i*pow10[pos-1] 这里的pos其实是向后推了一位的
rt.power=(rt.power + nx.power + i * i %mod * pow10[pos-1] %mod * pow10[pos-1] %mod *nx.cnt %mod) %mod;
//+2*x*y
rt.power=(rt.power+ 2 * i * pow10[pos-1] %mod * nx.sum %mod) %mod;
}
if(!lim) dp[pos][sum][mod1]=rt;
return rt;
}
ll solve(ll x)
{
int cnt=0;
while(x){
num[++cnt]=x%10; x/=10;
}
node rt=dfs(cnt+1,0,0,1);//cnt+1!!
//因为dfs中的终止条件是pos==1 所以这里应该是cnt+1
return rt.power %mod;
}
int main()
{
freopen("seven.in","r",stdin);
freopen("seven.out","w",stdout);
int T; ll l,r;
pow10[1]=1;
for(int i=2;i<=20;i++) pow10[i]=pow10[i-1]*10%mod;
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&l,&r);
ll ans=solve(r)-solve(l-1);
ans=(ans+mod)%mod;
printf("%lld
",ans);
}
}
/*
3
1 9
10 11
17 17
*/