第一次碰到这种题,才发现对数位DP真是不了解
不清楚状态就胡乱转移,还一直以为对了…
定 义 d p [ l e n ] [ m o ] [ w e i m o ] 为 长 l e n , 且 数 字 总 和 模 为 m o , 数 字 位 总 和 为 w e i m o 的 状 态 定义dp[len][mo][weimo]为长len,且数字总和模为mo,数字位总和为weimo的状态 定义dp[len][mo][weimo]为长len,且数字总和模为mo,数字位总和为weimo的状态
然 后 你 可 能 想 直 接 在 d p [ l e n ] [ m o ] [ w e i m o ] 放 合 法 数 的 平 方 和 然后你可能想直接在dp[len][mo][weimo]放合法数的平方和 然后你可能想直接在dp[len][mo][weimo]放合法数的平方和
但 是 怎 么 转 移 ? 但是怎么转移? 但是怎么转移?
一 般 情 况 是 这 样 做 \color{Red}一般情况是这样做 一般情况是这样做
for(int i=0;i<=last;i++)
{
int MO=(mo*10+i)%7;
int WEIMO=(weimo+i)%7;
sumn+=dfs(len-1,has7||(i==7),MO,WEIMO,limit&&(i==a[len]),he*10+i);
sumn%=mod;
}
但 是 在 这 里 , 怎 么 能 直 接 加 上 去 呢 ? \color{Red}但是在这里,怎么能直接加上去呢? 但是在这里,怎么能直接加上去呢?
s u m n 加 上 的 数 表 示 长 度 为 l e n − 1 的 平 方 和 , 没 有 算 上 我 们 之 前 选 的 数 字 啊 ! ! ! sumn加上的数表示长度为len{-}1的平方和,没有算上我们之前选的数字啊!!! sumn加上的数表示长度为len−1的平方和,没有算上我们之前选的数字啊!!!
让我们分析一下
设 当 前 选 到 第 l e n 个 位 置 , 这 一 位 选 的 数 字 是 i 设当前选到第len个位置,这一位选的数字是i 设当前选到第len个位置,这一位选的数字是i
由 于 这 一 位 大 小 是 i ∗ 1 0 l e n − 1 记 作 y , 设 后 面 l e n − 1 位 合 法 数 是 x j 由于这一位大小是i*10^{len-1}记作y,设后面len-1位合法数是x_j 由于这一位大小是i∗10len−1记作y,设后面len−1位合法数是xj
那 么 平 方 和 是 ∑ x j ∈ 合 法 数 字 ( y + x j ) 2 那么平方和是\sum_{x_j\in合法数字}(y+x_j)^2 那么平方和是∑xj∈合法数字(y+xj)2
化 简 得 ∑ x j ∈ 合 法 数 字 y 2 + x j 2 + 2 y x j 化简得\sum_{x_j\in合法数字}y^2+x_j^2+2yx_j 化简得∑xj∈合法数字y2+xj2+2yxj
设 合 法 数 字 个 数 是 c n t , 那 么 有 设合法数字个数是cnt,那么有 设合法数字个数是cnt,那么有
c n t ∗ y 2 + ∑ x j 2 + 2 y ∑ x j cnt*y^2+\sum{x_j^2+2y}\sum{x_j} cnt∗y2+∑xj2+2y∑xj
所 以 在 每 个 d p 的 状 态 种 储 存 三 个 值 才 能 进 行 转 移 所以在每个dp的状态种储存三个值才能进行转移 所以在每个dp的状态种储存三个值才能进行转移
Ⅰ . 当 前 状 态 合 法 数 字 的 个 数 , 也 就 是 上 面 的 c n t Ⅰ.当前状态合法数字的个数,也就是上面的cnt Ⅰ.当前状态合法数字的个数,也就是上面的cnt
Ⅱ . 当 前 状 态 的 合 法 数 的 平 方 和 Ⅱ.当前状态的合法数的平方和 Ⅱ.当前状态的合法数的平方和
Ⅲ . 当 前 状 态 合 法 数 字 的 和 Ⅲ.当前状态合法数字的和 Ⅲ.当前状态合法数字的和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
struct p{
ll SUM,cnt,qsum;
p():cnt(0),SUM(0),qsum(0){}
p(ll a,ll v,ll c){
SUM=a,cnt=v,qsum=c;
}
};
ll n,t,a[22],ten[22];
p dp[20][7][7];
p dfs(int len,int mo,int weimo,int limit)
{
if( len==0 )
{
if( mo==0||weimo==0 ) return (p){0,0,0};
return (p){0,1,0};
}
p now=dp[len][mo][weimo],ans;
if( !limit&&now.SUM!=0 ) return now;
int last=limit?a[len]:9;
for(int i=0;i<=last;i++)
{
if( i==7 ) continue;
ll MO=(mo*10+i)%7, MOWEI=(weimo+i)%7;
p temp=dfs(len-1,MO,MOWEI,limit&&(i==a[len]) );
ll res=i*ten[len-1]%mod;
ans.cnt=( ans.cnt+temp.cnt )%mod;
ans.SUM=( ans.SUM+temp.SUM+res*temp.cnt%mod )%mod;//当前状态数字和
ll k1=res*res%mod*temp.cnt%mod;//cnt*x^2
ll k2=2*res%mod*temp.SUM%mod;
ans.qsum=( ans.qsum+k1+k2+temp.qsum)%mod;//当前状态平方和
}
if( !limit ) dp[len][mo][weimo]=ans;
return ans;
}
ll sovle(ll n)
{
memset(a,0,sizeof(a));
while( n )
{
a[++a[0]]=n%10;
n/=10;
}
return dfs(a[0],0,0,1).qsum;
}
int main()
{
ten[0]=1;
for(int i=1;i<=18;i++) ten[i]=ten[i-1]*10;
cin >> t;
while( t-- )
{
ll l,r;
cin >> l >> r;
cout << (mod+sovle(r)-sovle(l-1))%mod << endl;
}
}