一般的数位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\n",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\n",ans); } } /* 3 1 9 10 11 17 17 */