数位 dp
ta 是邪恶的数位 dp,我的午饭终结者 
(调着调着就忘记午饭了)
首先看一道例题
// 学习 www.luogu.com.cn/article/qbp4ezkk P2602 的题解
// %%% dalao 

Example 01 [P2602 数字统计][l,r] 中每个数字出现了多少次
(1 <= l,r <= 1e12)

Solution 
这题直接 for 肯定会 TLE
我们思考用一个 dp 数组
dp[i][j][k] 表示搜到第 i 位,保证最高位为 j,数字 k 的出现次数
(比如 dp[2][1][1] 就表示 [0,19] 有多少个 1) 
显然区间比较难
我们借用前缀和思想
将 ans[l,r] 转化为 ans[1,r]-ans[1,l-1] 即可

首先得到一个方程 
dp[i][j][k][j...00~j...99]中 k 的次数
所以 dp[i][j][k] = 所有 j 中 k 出现次数 + [000000000~999999999] 中 k 出现次数
				 = 所有 j 中 k 出现次数 + sum(dp[i-1][iterator][k]) [0~9] 
				 
现在我们只差第一部分怎么求 
如果 j!=k 第一部分肯定是 0
否则就是   1e(i-1) 
所以完整版的转移方程即为

dp[i][j][k]={ sum(dp[i-1][iterator][k]) [0~9]              (j!=k)
			{ 1e(i-1) + sum(dp[i-1][iterator][k]) [0~9]    (j==k)

这个肯定是能求的
于是有如下 Code

const unsigned long long ten[16]={1,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15};
unsigned long long number[16][11][11]; 
void _memset(){
	//明显如果只有 0 位,那么一定只有 0 个 
	for(int i=0;i<=9;i++)number[1][i][i]=1;//初始化状态 
	for(int i=2;i<=13;i++)for(int j=0;j<=9;j++)for(int k=1;k<=9;k++){//枚举 i,j,k 
		if(j==k)number[i][j][k]+=ten[i-1];		
		for(int it=0;it<=9;it++)number[i][j][k]+=number[i-1][it][k];
	} 
} 

这部分非常简单
接下来考虑如何求解
比如说 6187
首先我们拆开每一位,变成 6 1 8 7 (C++ 语法入门) 
接下来统计三种
# 01 位数与原数一样,高位较小的数 (for 循环使用) 
ans+=[1000,1999]+[2000,2999]+[3000,3999]+[4000,4999]+[5000,5999] 
# 02 位数较小,无前导零 (for 循环使用) 
ans+=[0]+...+[9]+[10,19]+[20,29]+....+[900,999] 

我们发现已经将 [0,5999] 的 ans 统计完了 
还差 [6000,6187] 
接下来我们直接将区间拆开
[6000,6099]
[6100,6109]+[6110,6119]+...+[6170,6179]
[6180]+[6181]+...+[6187]
我们再将每个区间劈开
k*[60]+[00,99] 
k*[610]+[0,9]+k*[611]+[0,9].......
.....
(太抽象了)

一波玄学统计
劈开的后一半好算
可前一半怎么算呢
以[6100,6179]为例 
明显 61 的次数 = 1e1*8
于是太离谱了,还是开码吧
#Warning	\*注意拆分数字的部分!                   *\
			\*最后一个数字不会计算所以最终需要 -1*\
			\*ten 数组不能直接 1e... 会CE!          *\
			
Code 
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const ULL ten[16]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000,10000000000,100000000000,1000000000000,10000000000000};
ULL number[16][11][11]={}; 
stack<int> st;
vector<int> digit;
void _memset(){
	//明显如果只有 0 位,那么一定只有 0 个 
	for(int i=0;i<=9;i++)number[1][i][i]=1;//初始化状态 
	for(int i=2;i<=12;i++)for(int j=0;j<=9;j++)for(int k=0;k<=9;k++){//枚举 i,j,k 
		if(j==k)number[i][j][k]+=ten[i-1];		
		for(int it=0;it<=9;it++)number[i][j][k]+=number[i-1][it][k];
	} 
}
ULL cut(ULL x,int k){//1~x 中 k 的出现次数 
	ULL ans=0;
	int len=0;
//拆开 
	digit.clear();
	digit.push_back(-1);
	while(x) st.push(x%10),x/=10,++len;
	while(!st.empty()) digit.push_back(st.top()),st.pop();
//统计第一部分 
//不带前导零从一开始!
	for(int i=1;i<digit[1];i++) ans+=number[len][i][k]; 
//统计第二部分
//只能到 len-1! 
	for(int it_len=1;it_len<len;it_len++)for(int i=1;i<=9;i++) ans+=number[it_len][i][k];
//统计第三部分
	for(int lock_len=1;lock_len<len;lock_len++){
		//统计劈开的后一半 
		for(int i=0;i<digit[lock_len+1];i++)ans+=number[len-lock_len][i][k];
		//统计劈开的前一半 
		for(int i=1;i<=lock_len;i++) if(digit[i]==k) ans+=digit[lock_len+1]*ten[len-lock_len-1];
	} 
	return ans; 
}
int main(){
	_memset();
	ULL lim,rim;
	scanf("%llu%llu",&lim,&rim);
	for(int i=0;i<=9;i++)printf("%llu ",cut(rim+1,i)-cut(lim,i));
//	printf("%d %d",cut(10,0),cut(1,0));
	return 0;
}


Question 01 [P2567 Windy Number]
定义任意相邻数字相差至少为 2 的数为 Windy Number
求 [l,r] 中 Windy Number 的个数

Solution 01
先不看题解推理一下
盲猜还是前缀和 minus 
首先显然我们思考能否求出一段
[000 ... 0000,999 ... 9999] 之中的 Windy 个数
发现显然是可以的
设 Windy[i][j] 为 i 位数字首位为 j 的 Windy 个数

那么显然有转移
Windy[i][j]=sum(Windy[i-1][k]) (0<=k<=j-2) (j+2<=k<=9)
而边界条件 Windy[1][i]=1 (0<=i<=9)
非常的 Nice

Code
//Get Windy 
void Wind(){
	for(int i=0;i<=9;i++)Windy[1][i]=1for(int len=2;len<=LIM;len++){
		for(int num=0;num<=9;num++){
			for(int it=0;it<=num-2;it++)Windy[len][num]+=Windy[len-1][it];
			for(int it=num+2;it<=9;it++)Windy[len][num]+=Windy[len-1][it];
		}
	} 
}


发现这题貌似也可以拆开
仍然是 Example 中的三部分
(以下引用 Example 中的 Solution) 

Sample 6112
# 01 位数与原数一样,高位较小的数 (for 循环使用) 
ans+=[1000,1999]+[2000,2999]+[3000,3999]+[4000,4999]+[5000,5999] 
# 02 位数较小,无前导零 (for 循环使用) 
ans+=[0]+...+[9]+[10,19]+[20,29]+....+[900,999] 
# 03 其余的几个数
做了几道题大家其实也发现了
这部分才是难点
现在思考如何求 [6000,6112] 中的 Windy 数
思考还拆成原来的 [6000,6099] [6100,6109] [6110] [6111] [6112]
每一次判断当前枚举位和上一位是否合法
合法就加上
特别的如果当前最大位不合法那就直接 return
so 6110 以后都不用算了 
记得一定要加一! 
(貌似这是我第一个不看题解 AC 的蓝题)
(其实难度之有绿的样子)

Code
#include<bits/stdc++.h>
using namespace std;
const int LIM=10;
typedef unsigned long long ULL;
ULL lim,rim,Windy[LIM+5][13];
//Get Windy 
void Wind(){
	for(int i=0;i<=9;i++)Windy[1][i]=1; 
	for(int len=2;len<=LIM;len++){
		for(int num=0;num<=9;num++){
			for(int it=0;it<=num-2;it++)Windy[len][num]+=Windy[len-1][it];
			for(int it=num+2;it<=9;it++)Windy[len][num]+=Windy[len-1][it];
		}
	} 
}
//拆数两件套 
stack<int> st;
vector<int> digit;
ULL Windy_Num(ULL pos){
	ULL ans=0;
	int len=0;
//拆数 
	digit.clear();
	digit.push_back(-1);
	while(pos) st.push(pos%10),pos/=10,++len;
	while(!st.empty()) digit.push_back(st.top()),st.pop();
//add #01
	for(int i=1;i<digit[1];i++)ans+=Windy[len][i];
//add #02
	for(int i=1;i<len;i++) for(int num=1;num<=9;num++)ans+=Windy[i][num];
//add #03
	for(int lock_len=1;lock_len<len;lock_len++){
		for(int num=0;num<digit[lock_len+1];num++){
			if(abs(digit[lock_len]-num)<2)continue;
			ans+=Windy[len-lock_len][num];
		}
		if(abs(digit[lock_len+1]-digit[lock_len])<2)return ans;
	} 
	return ans;
} 
int main(){
	Wind();
	scanf("%llu%llu",&lim,&rim);
//	for(int i=lim;i<=rim;i++)printf("%d:%llu\n",i,Windy_Num(i+1));
	printf("%llu",Windy_Num(rim+1)-Windy_Num(lim));
	return 0;
}

Question 02 [ACP2195 Amount Of Degrees]
求一段区间 [L,R] 内满足下列条件的整数个数:
这个数恰好等于 K 个互不相等的 B 的整数次幂之和。
1<=L<=R<=INT_MAX
Solution
再试一次不看题解拿 AC
首先这题和原来有一点不一样
我们发现这个到最后其实就是把 L R 转化成一个 B 进制数
并且其中刚好有 K 个一
一波玄学思考之后我们发现在拆开每一位的时候搞点花活就可以了
接下来设 dp[i][k][0000000-???????] 中有k个一的个数
则转移不难
DP[i][k]=DP[i-1][k]*(B-1)+DP[i-1][k-1]
接下来就是拆数字
还是以 6112 为例
(B=10[0000,0999](k) =[000,999](k)
[1000,1999](k) =[000,999](k-1)
[2000,2999](k) =[000,999](k)
[3000,3999](k) =[000,999](k)
[4000,4999](k) =[000,999](k)
[5000,5999](k) =[000,999](k)
[6000,6099](k) =[00,99](k)
[6100,6109](k) =[0,9](k-1)
[6110]
[6111]
[6112]
然后作者开码写出了下面的玩意

Code Warning:并非正确代码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int LIM=70;
int K,B;
ULL Pow[LIM+3],dp[LIM+3][LIM+3],L,R;
void MemSet(){
	Pow[0]=1;
	for(int i=1;i<=LIM;i++)Pow[i]=Pow[i-1]*10,dp[0][0]=1;
	for(int i=1;i<=LIM;i++){
		for(int j=0;j<=i;j++){
			dp[i][j]=dp[i-1][j]*(B-1);
			if(j)dp[i][j]+=dp[i-1][j-1];
		}
	}
}
stack<int>st;
vector<int> k;
int len;
ULL ans(ULL x){
	k.clear(),k.push_back(0);
	while(x) st.push(x%B),x/=B;
	while(!st.empty()) k.push_back(st.top()),st.pop();
	len=k.size()-1;
	ULL sum=0;
	int cnt=0,r;
	for(int l=0;l<len;l++){
		for(int i=0;i<k[l+1];i++){
			r=K-cnt;
			if(i==1)--r;
			if(r<0)continue;
			sum+=dp[len-l-1][r];
		}
		if(k[l+1]==1)++cnt;
		if(cnt>K)break;
	}
	return sum;
}
int main(){
	scanf("%llu%llu%d%d",&L,&R,&K,&B);
	MemSet();
	printf("%llu\n",ans(R+1)-ans(L));
}

submit,WA 60pts
哪里错了呢?
再看一眼题目
WC!其余位数根据题意必须为 0
(此处并非有意坑人,实属作者亲身经历)
转移方程改吧改吧
DP[i][k]=DP[i-1][k]+DP[i-1][k-1]
最后拆数的时候加个判断不满足最高位为一直接 break

AC Code
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int LIM=70;
int K,B;
ULL dp[LIM+3][LIM+3]={},L,R;
void MemSet(){
	dp[0][0]=1;
	for(int i=1;i<=LIM;i++){
		for(int j=0;j<=i;j++){
			dp[i][j]=dp[i-1][j];
			if(j)dp[i][j]+=dp[i-1][j-1];
		}
	}
}
stack<int>st;
vector<int> k;
int len;
ULL ans(ULL x){
	k.clear(),k.push_back(0);
	while(x) st.push(x%B),x/=B;
	while(!st.empty()) k.push_back(st.top()),st.pop();
	len=k.size()-1;
	ULL sum=0;
	int cnt=0,r;
	for(int l=0;l<len;l++){
		for(int i=0;i<k[l+1]&&i<2;i++){
			r=K-cnt;
			if(i==1)--r;
			if(r<0)continue;
			sum+=dp[len-l-1][r];
		//	printf("dp[%d][%d]=%llu added to sum!\n",len-l-1,r,dp[len-l-1][r]);
		}
		if(k[l+1]==1)++cnt;
		if(cnt>K)break;
		if(k[l+1]>1)break;
	}
	return sum;
}
int main(){
	scanf("%llu%llu%d%d",&L,&R,&K,&B);
	MemSet();
	printf("%llu\n",ans(R+1)-ans(L));
}

Question 03 [ACP2196 数字游戏][L,R] 直间满足各数位从左到右单调不降的数的个数
有多组测试数据。每组只含两个数字
Solution
Windy 数简单版本
DP[i][j] 表示 [j00000000,j99999999] 中满足条件的个数
转移方程 DP[i][j]=sum{DP[i-1][k]} (k>=j)
边界设 Dp[1][j]=1
其余和 Windy 数基本一样不细写了,看代码吧
(Dalao:无良作者,举报!)

Tips:
1. 记得是多测!
2. 如果下一位小于当前位一定要 Break! 这不是可有可无的剪枝!
   因为如果不这样譬如 613 614 这样的答案也会计入从而让你喜提 WA 0pts
   (LOJ 单测试点让人暖心)
Code
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int LIM=18;
ULL dp[LIM+3][10];
void MemSet(){
	for(int i=0;i<=9;i++)dp[1][i]=1;
	for(int l=2;l<=LIM;l++)for(int i=0;i<=9;i++)for(int j=i;j<=9;j++)dp[l][i]+=dp[l-1][j];
}
stack<int> st;
vector<int> k;
int len;
ULL ans(ULL x){
	k.clear(),k.push_back(0);
	while(x)st.push(x%10),x/=10;
	while(!st.empty())k.push_back(st.top()),st.pop();
	len=k.size()-1;
	ULL sum=0;
	for(int l=0;l<len;l++){
		if(k[l+1]<k[l])break;
		for(int i=k[l];i<k[l+1];i++){
			sum+=dp[len-l][i];
		}
	}
	return sum;
}
int main(){
	ULL L,R;
	MemSet();
	while(scanf("%llu%llu",&L,&R)!=EOF) printf("%llu\n",ans(R+1)-ans(L));
	return 0;
}

Question 04 [ACP2198 取模数]
由于科协里最近真的很流行数字游戏,某人又命名了一种取模数,这种数字必须满足各位数字之和 mod N 为 0。
现在大家又要玩游戏了,指定一个整数闭区间 [a,b],问这个区间内有多少个取模数。
Solution
思考一下发现可以设 DP[i][j][k] 表示共有 i 位,首位为 j,模 N 为 k 的个数
转移也不难
DP[i][j][k]=Sum{DP[i-1][iterator][k-i]}
这个大致是对的
但不要忘了取模和处理负数
边界也好写:dp[1][j][j%N]=1 即可
多测不清空,爆零两行泪!

Code
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int LIM=17,M=108;
ULL dp[LIM+4][10][M+4];
int K;
void MemSet(){
	for(int i=0;i<=LIM;i++)for(int j=0;j<=9;j++)for(int tmp=0;tmp<=M;tmp++)dp[i][j][tmp]=0;
	for(int i=0;i<=9;i++)dp[1][i][i%K]=1;
	for(int l=2;l<=LIM;l++)for(int i=0;i<=9;i++)for(int it=0;it<K;it++)for(int j=0;j<=9;j++)dp[l][i][it]+=dp[l-1][j][((it-i)%K+K)%K];
}
vector<int> k;
stack<int> st;
ULL ans(ULL x){
	k.clear(),k.push_back(0);
	while(x)st.push(x%10),x/=10;
	while(!st.empty())k.push_back(st.top()),st.pop();
	int len=k.size()-1,cnt=k[1]%K;
	ULL sum=0;
	for(int l=1;l<len;l++)for(int i=1;i<=9;i++)sum+=dp[l][i][0];//,printf("dp[%d][%d][%d]=%llu added!\n",l,i,0,dp[l][i][0]);
	for(int i=1;i<k[1];i++)sum+=dp[len][i][0];//,printf("dp[%d][%d][%d]=%llu added!\n",len,i,0,dp[len][i][0]);
	for(int l=1;l<len;l++){
		for(int i=0;i<k[l+1];i++)sum+=dp[len-l][i][(K-cnt)%K];//,printf("dp[%d][%d][%d]=%llu added!\n",len-l,i,(K-cnt)%K,dp[len-l][i][(K-cnt)%K]);
		cnt=(cnt+k[l+1])%K;
	}
	return sum;
}
int main(){
	ULL L,R;
	while(scanf("%llu%llu%d",&L,&R,&K)!=EOF){
		MemSet();
		printf("%llu\n",ans(R+1)-ans(L));
	}
	return 0;
}

Question 05 [ACP2199 不要62]
求给定的 [L,R] 之间不含有 62 连号或含有 4 的数目
多测 0 < L <= R <= 1e7
Solution
1e7O(n log n)
你 LOJ 神机差不多能过
但 ACC 肯定不行
还是正经写数位 DP 吧
这题和以前见得 数位 DP 不太一样
正难则反
找不吉利的有多少用总数减去即可
思考如果当前位是 4 直接 += 1e...
如果当前位是 6 就把除了下一位是二的不吉利数字加上
再加上二下一位所有的数字
否则直接求 sum 即可
设 DP[i][j] 为已经来到第 i 位首位为 j
则有以下方程
DP[i][j]={ 10^(i-1)                       i==4
         { sum{dp[i-1][除了2]}+10^(i-2)   i==6
         { sum{dp[i-1][0~9]               其余情况
剩下的已经很熟悉了我就不 BB 了
开码!

Code
#include<bits/stdc++.h>
using namespace std;
const int LIM=7;
int dp[LIM+5][10],ten[LIM+5];
void MemSet(){
	dp[1][4]=1,ten[0]=1;
	for(int i=1;i<=LIM;i++)ten[i]=ten[i-1]*10;
	for(int l=2;l<=LIM;l++)for(int i=0;i<=9;i++){
		if(i==4){dp[l][i]=ten[l-1];continue;}
		for(int it=0;it<=9;it++)dp[l][i]+=dp[l-1][it];
		if(i==6)dp[l][i]=dp[l][i]-dp[l-1][2]+ten[l-2];
	}
}
stack<int> st;
vector<int> k;
int ans(int x){
	k.clear(),k.push_back(0);
	while(x)st.push(x%10),x/=10;
	while(!st.empty())k.push_back(st.top()),st.pop();
	int len=k.size()-1,sum=0;
	bool flag=0;
	for(int l=1;l<len;l++)for(int i=1;i<=9;i++)sum+=dp[l][i];
	for(int i=1;i<k[1];i++)sum+=dp[len][i];
	for(int l=1;l<len;l++){
		if(k[l]==4||(k[l-1]==6&&k[l]==2))flag=1;
		for(int i=0;i<k[l+1];i++){
			if(flag)sum+=ten[len-l-1];
			else if(k[l]==6&&i==2)sum+=ten[len-l-1];
			else sum+=dp[len-l][i];
		}
	}
	return sum;
}
int main(){
	int l,r;
	MemSet();
	while(true){
		scanf("%d%d",&l,&r);
		if(l+r==0)return 0;
		printf("%d\n",r-l+1-ans(r+1)+ans(l));
	}
}

Question 06 [ACP2200 恨7不成妻]
如果一个整数符合下面三个条件之一,那么我们就说这个整数和 7 有关:
1. 整数中某一位是 7
2. 整数的每一位加起来的和是 7 的整数倍
3. 这个整数是 7 的整数倍
求 [L,R] 内和 7 无关的数字的平方和模 1e9+7 的值。
多测 0 < L <= R <= 1e18

Solution
思考发现限制 1,2 就是上一题和上上题
限制 3 怎么搞?
平方和又怎么搞?

[1,n] 平方和经查是 n(n+1)(2n+1)/6
限制 3 经过思考(看了眼题解)得到可以在 DP 数组中加一维记录然后就可以了 QwQ

设 DP[i][j][k][l][共有 i 位 & 首位为 j &7 余 k & 所有数字加和模 7 得 l] 的数字平方和(Look here!)
但这就产生了一个很尴尬的事情
就是限制 1 不太好搞
我们不好给他放进DP数组里
思考上一题 Code 里的 Flag 操作
为了避免写一些恶心无比的容斥原理
我们正难则反的思路也被放弃
而是直接在 DP 的时候就把七跳过
最后遇到七也直接舍弃后面的答案 Break 掉

可是事情到这里并未结束
现在思考我们已经知道 a^2+b^2+c^2+... 
如何求出 (a+K)^2+... 呢?
古希腊先贤柏拉图曾说过:巧妇饿不死瞎家雀
所以我们展开原式得到 a^2+b^2+c^2+...+2aK+2bK+2cK+...+K^2+K^2+K^2+...
设 Qsum 为a^2+b^2+c^2+...,即所有数的平方和
设 sum 为a+b+c+d...,即所有数的和
设 cnt 为这些数的个数 (比如 a,b,c cnt=3)
一波大力落奇迹之后 得到 
原式=Qsum+2*sum*K+cnt*K*K
其中 Qsum 和 K 为已知
cnt 也容易维护
思考 sum 咋整
也不难,直接 +=cnt*K 即可
Warning!
Warning!
Warning!
此处需要先更新 Qsum 再更新 sum!
(MarkDown 的卡了我三个小时)

所以我们可以写一下DP转移方程了
K=10^(i-1)*j
iterator=[0,6]+[8,9]
DP_cnt[i][j][k][l]=SUM{DP_cnt[i-1][iterator][((k-10^(i-1)*j)%7+7)%7][((l-i)%7+7)%7]}
DP_sum[现在(ijkl)]=SUM{DP_sum[同上]}+K*DP_cnt[现在]
DP_Qsum[现在]=SUM{DP_Qsum[同上]}+2*DP_sum[现在]*K+DP_cnt[现在]*K*K
拆数之类的基本和前面一样不做赘述
然后就没有了
开码!
Code
#include<bits/stdc++.h>
#define loop(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef unsigned long long ULL;
const ULL MOD=1e9+7;
const int LIM=18;
ULL Q[LIM+5][10][7][7],S[LIM+5][10][7][7],C[LIM+5][10][7][7],ten[LIM+5];//对应文中 Qsum sum cnt
void MemSet(){
	ten[0]=1;
	loop(i,1,LIM) ten[i]=ten[i-1]*10;
	loop(i,0,9){
		if(i==7)continue;
		C[1][i][i%7][i%7]=1;
		S[1][i][i%7][i%7]=i;
		Q[1][i][i%7][i%7]=i*i;
	}
	ULL K;
	int aa,bb,kk;
	loop(i,2,LIM) loop(j,0,9){
		if(j==7)continue;
		K=ten[i-1]*j;kk=K%7;K%=MOD;
		loop(a,0,6) loop(b,0,6){
			aa=(a-kk+70)%7,bb=(b-j+70)%7;
			loop(it,0,9){
				if(it==7)continue;
				C[i][j][a][b]=(C[i][j][a][b]+C[i-1][it][aa][bb])%MOD;
				S[i][j][a][b]=(S[i][j][a][b]+S[i-1][it][aa][bb])%MOD;
				Q[i][j][a][b]=(Q[i][j][a][b]+Q[i-1][it][aa][bb])%MOD;
				//if(Q[i-1][it][aa][bb])printf("Get Q[%d][%d][%d][%d] = %llu\n",i-1,it,aa,bb,Q[i-1][it][aa][bb]);
			}
			Q[i][j][a][b]=(S[i][j][a][b]*K%MOD*2%MOD+C[i][j][a][b]*K%MOD*K%MOD+Q[i][j][a][b])%MOD;
			S[i][j][a][b]=(C[i][j][a][b]*K%MOD+S[i][j][a][b])%MOD;
			//if(Q[i][j][a][b]) printf("%d %d %d %d C:%llu S:%llu Q:%llu\n",i,j,a,b,C[i][j][a][b],S[i][j][a][b],Q[i][j][a][b]);
		}
	}  
	
}
vector<int> k;
stack<int> st;
inline ULL ans(ULL x){
	k.clear(),k.push_back(0);
	while(x)st.push(x%10),x/=10;
	while(!st.empty())k.push_back(st.top()),st.pop();
	int len=k.size()-1,tot=0,tot2=0;
	ULL sum=0,cnt=0,tmp;
	loop(l,0,len-1){
		loop(i,0,k[l+1]-1){
			if(i==7)continue;
			loop(a,0,6) {
				loop(b,0,6){
					if((tot2+a)%7==0)continue;
					if((tot+b)%7==0)continue;
					tmp=(S[len-l][i][a][b]*cnt%MOD*2%MOD+C[len-l][i][a][b]*cnt%MOD*cnt%MOD+Q[len-l][i][a][b])%MOD;
					sum=(tmp+sum)%MOD;
				}
			}
		}
		if(k[l+1]==7)break;
		cnt+=ten[len-l-1]%MOD*k[l+1]%MOD;
		tot=(tot+k[l+1])%7;
		tot2=(tot2+(k[l+1]%7)*(ten[len-l-1]%7))%7;
	}
	return sum;
}
int main(){
	int T;
	ULL L,R;
	MemSet();
	scanf("%d",&T);
	while(T--){
		scanf("%llu%llu",&L,&R);
		printf("%llu\n",(ans(R+1)+MOD-ans(L))%MOD);
	}
	return 0;
}
posted on   2025ing  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示