第一次碰到这种题,才发现对数位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的平方和,没有算上我们之前选的数字啊!!! sumnlen1,!!!

让我们分析一下

设 当 前 选 到 第 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 i10len1y,len1xj

那 么 平 方 和 是 ∑ 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 xjy2+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} cnty2+xj2+2yxj

所 以 在 每 个 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;
	}
}