题意
2013腾讯编程马拉松初赛第一场(3月21日)的一道题,有一定的难度。
求[X,Y]中的满足与7无关的数的平方和,一个数满足如下条件之一为与7有关:
- 某一位是7
- 每一位相加是7的倍数
- 这个数是7的倍数
思路
考察数位DP,但是难点在求平方和。
数位DP一般有两种做法,一种是预处理的方法,之前的题目都是使用预处理的方法求的,还有一种方法是DFS,这道题使用预处理方法做要比DFS麻烦很多。
这题如果只是求满足条件的个数的话就是一道模板题,难在平方和的递推。
f[i][j][a][b]
表示第i位填j,位数之和模7为a,整个数模7为b;
枚举第i-1位,假设第i-1位填k,用状态表示为f[i-1][k][(a+k)%7][(b*10+k)%7]
。
现在考虑平方和的递推:
假设第i位位A,\(AB_1,AB_2,\dots,AB_n\)都为满足条件的数,平方和为:
先考虑两个数
\((A*10^{i-1}+B_1)^2+(A*10^{i-1}+B_2)^2\)
\(=2*(A*10^{i-1})^2+2*(A*10^{i-1})*(B_1+B_2)+(B_1^2+B_2^2)\)
扩展到cnt个数就是\(cnt*(A*10^{i-1})^2+2*(A*10^{i-1})*(B_1+B_2+..+B_n)+(B_1^2+B_2^2+...+B_n^2)\)
从多项式可以看出要得到第i位为A时满足条件的数的平方和,需要求得前i-1位满足条件的数的个数、每个满足条件的数之和、每个满足条件的数的平方和。
用结构体f[i][a][b]
维护,前i位,各个数字之和模7为a,整个数模7为b的满足条件的数的个数cnt,每个数之和sum,每个数的平方和squ。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
struct node{
LL cnt;
LL sum,squ;//个数,和,平方
}f[20][7][7];
const int P=1e9+7;
LL a[20];
LL ten[20];
int t;
inline node dfs(LL pos,LL add,LL x,bool limit){
if(!pos){
LL cnt=0;
if(add&&x)cnt++;
return node{cnt,0,0};
}
if(!limit&&f[pos][add][x].cnt!=-1)return f[pos][add][x];
LL up=limit?a[pos]:9;
node ans={0,0,0};
for(int i=0;i<=up;i++){
if(i==7)continue;
node tmp=dfs(pos-1,(add+i)%7,(x*10+i)%7,limit&&i==a[pos]);
LL k=ten[pos]*i%P;
ans.cnt=(ans.cnt+tmp.cnt)%P;
ans.sum=(ans.sum+k*tmp.cnt%P+tmp.sum)%P;
//a^2+2ab+b^2
ans.squ=(ans.squ+k*k%P*tmp.cnt)%P;
ans.squ=(ans.squ+2*k%P*tmp.sum%P)%P;
ans.squ=(ans.squ+tmp.squ)%P;
}
return limit?ans:f[pos][add][x]=ans;
}
inline LL sol(LL n){
LL pos=0;
while (n)a[++pos]=n%10,n/=10;
memset(f,-1,sizeof f);
return dfs(pos,0,0,true).squ%P;
}
int main(){
cin>>t;
LL l,r;
//预处理10的幂
ten[1]=1;
for(int i=2;i<20;i++)ten[i]=ten[i-1]*10%P;
while (t--){
//cout<<"t "<< t<<endl;
cin>>l>>r;
cout<<(sol(r)-sol(l-1)+P)%P<<endl;
}
return 0;
}