取自洛谷题解
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
唯一难点在于处理平方和,其思想在于使用平方和公式记录平方和以此达到目的。例如2312使用的是
200^2+31^2+2*200*31
这种方式
而对于同一位下的多个可能的下属位(例如同一个十位下的多种个位)则可以一并计算
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll l,r;
struct Node
{
ll num,sum,sqsum;//num记录一位的下位有多少种填法,sum记录这些种填法的数的总和,sqsum记录这些种填法的数的总平方和
}dp[20][10][10];//分别记录位数,各位数和%7,本身%7
int d[20],len,t;//d[]记录各个数位
ll p[20];//p[]预处理10^i
const ll mod=1e9+7;
Node dfs(int v,int sum,int num,bool flag)
{
if(v<=0)
{
Node tmp;
tmp.num=(num!=0&&sum!=0);//只有数位和与数本身都不乘除7时为1
tmp.sum=0;
tmp.sqsum=0;
return tmp;
}
if(!flag&&dp[v][sum][num].num!=-1)
return dp[v][sum][num];
int up=flag ? d[v]:9;
Node ans;
ans.num=ans.sum=ans.sqsum=0;
for(int i=0; i<=up; i++)
{
if(i==7) continue;//任意位为7时不取
Node tmp=dfs(v-1,(sum+i)%7,(num*10+i)%7,flag&&(i==up));
ans.num+=tmp.num;//记录下属个数
ans.num%=mod;
ll newU=p[v-1]*i%mod;//记录本位值的值(如例中231的200部分)
ans.sum+=(tmp.sum+newU*tmp.num%mod)%mod;
//注意本位要与下属个数相乘,一一对应
ans.sum%=mod;
ans.sqsum+=((tmp.sqsum+2*newU*tmp.sum%mod)%mod+tmp.num*newU%mod*newU%mod)%mod;
//tmp.sqsum为下属位的平方和,此式即31^2+2*200*31+200^2
//注意此处也为多次计算合并一次,本位平方要与下属个数相乘
ans.sqsum%=mod;
}
if(!flag)
dp[v][sum][num]=ans;
return ans;
}
void change(ll num)
{
len=0;
while(num)
{
d[++len]=num%10;
num/=10;
}
}
ll solve(ll num)
{
change(num);
return dfs(len,0,0,1).sqsum;
}
int main()
{
memset(dp,-1,sizeof(dp));
p[0]=1;
for(int i=1;i<20;i++)
{
p[i]=(p[i-1]*10)%mod;
}
scanf("%d",&t);
while(t--)
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",(solve(r)-solve(l-1)+mod)%mod);
//强调注意结果由于取模有可能为负值,需向正取模
}
return 0;
}