题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=4507
一开始看到这题,数位dp,最初的想法是len为0的时候,只要符合要求,就返回之前组合成的数的平方,就像下面这样:
LL dfs(int len,int mod1,int mod2,bool flag,LL sum){
if(len==0){
if(mod1!=0&&mod2!=0) return (sum*sum)%MOD;
return 0;
}
int mlen=flag?bit[len]:9;
LL ret=0;
for(int i=0;i<=mlen;++i){
if(i==7) continue;
ret=(ret+dfs(len-1,(i+mod1)%7,(mod2*10+i)%7,flag&&i==mlen,(sum*10+i)%MOD))%MOD;
}
return ret;
}
但这样太暴力了,很明显会tle,于是很简单就想到记忆化搜索,但是怎么记忆化搜索呢?单单加一个dp数组吗?读者不妨自己试下,试了你就知道会wa了,原因是因为一开始数位dp是从0开始的,因此我们dp用的数组(假设这个数组是这样定义的:dp[len][mod1][mod2])存的是前几位是0(len以前的数字都是0),长度为len的数字中满足条件的解。而根据题意,所求答案不仅仅和目前讨论的位数有关,还和前面已讨论的数字有关。所以单单返回之前计算出的数的平方和的思路是错误的。
所以接下来的问题是:用记忆化搜索的形式加速数位dp,但是记忆化搜索要记录什么信息呢?
因为记忆化搜索只能记录目前讨论的数位的信息,不可以和已讨论的数位有联系(这样的话每次数位dp的结果不仅仅和目前讨论数位有关,数组无法记录)。所以想到用结构体同时保存三个信息:目前讨论条件下,符合要求的数的个数cnt,符合要求的数的和sum,符合要求的数的平方和sqrsum。设base为目前讨论的数的前缀(就是下面代码里的f),ai为后面各个符合要求的后缀。有如下式子:
∑ai就是sum,∑ai^2就是sqrsum,于是代码就很好写了:
#include<cstdio>
#include<iostream>
#include<cstring>
typedef long long LL;
using namespace std;
const int MOD=1e9+7;
#define MS(x,y) memset(x,y,sizeof(x))
const int MAXN=40;
int bit[MAXN],len;
LL pow[MAXN];
struct Node{
LL cnt,sum,sqrsum;
bool vis;
}dp[MAXN][7][7];
Node dfs(int len,int mod1,int mod2,bool flag){
if(len==0){
if(mod1&&mod2) return (Node){ 1,0,0,0 };
else return (Node){ 0,0,0,0 };
}
if(!flag&&dp[len][mod1][mod2].vis) return dp[len][mod1][mod2];
int mlen=flag?bit[len]:9;
Node ret=(Node){ 0,0,0,0 },nxt;
for(int i=0;i<=mlen;++i){
if(i==7) continue;
nxt=dfs(len-1,(i+mod1)%7,(mod2*10+i)%7,flag&&i==mlen);
LL f=i*pow[len-1]%MOD;
ret.cnt=(ret.cnt+nxt.cnt)%MOD;
ret.sum=(ret.sum+(nxt.sum+nxt.cnt*f)%MOD)%MOD;
ret.sqrsum=(ret.sqrsum+((nxt.cnt*f%MOD*f%MOD+2*f*nxt.sum%MOD)%MOD+nxt.sqrsum)%MOD)%MOD;
}
if(!flag) ret.vis=true,dp[len][mod1][mod2]=ret;
return ret;
}
LL solve(LL x){
len=0;
while(x) bit[++len]=x%10,x/=10;
return dfs(len,0,0,true).sqrsum;
}
int main(){
pow[0]=1;
for(int i=1;i<MAXN;++i) pow[i]=10*pow[i-1];
MS(dp,0);
int T;
scanf("%d",&T);
while(T--){
LL l,r;
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",((solve(r)-solve(l-1))%MOD+MOD)%MOD);
}
}