题目链接:https://vjudge.net/contest/308832#problem/G
题目大意:题目大意:求指定范围内与7不沾边的所有数的平方和。结果要mod 10^9+7。
转大佬:https://www.cnblogs.com/neopenx/p/4008921.html
与7不沾边的数需要满足三个条件。
①不出现7
②各位数和不是7的倍数
③这个数不是7的倍数
但是这题要统计的不是符合条件个数,而是平方和。
也就是说在DP时候,要重建每个数,算出平方,然后求和。
需要维护三个值(推荐使用结构体), 假定dfs推出返回的结构体是next,当前结果的结构体是ans。
其中①是基础数位DP。
②next.sum+(10 ^ len * i) * ans.cnt
③首先重建一下这个数,(10^len * i + x),其中x是这个数的后面部分,则平方和就是(10^len* i)^2+x ^ 2+2 * 10^len * i * x,其中x^2=next.sqsum
整体还要乘以next.cnt,毕竟不止一个。
这样sqsum+=next.sqsum
sqsum+=(2 * i * 10^len) * next.sum(神奇的化简)
sqsum+=(i * 10 ^ len ) ^ 2 * next.cnt
这个公式可以推一下:就是一个平方项展开。
例如:12
根据数位dp, a=10, b=2
那么(a+b) ^ 2 = a ^ 2 + b ^ 2 + 2 * a * b
如果有n个:powsum = (a+b1)^2 + (a+b2)^2 + … + (a+bn)^2 = n * a ^2 +n * b ^ 2 +2* n* a* (b1+b2+…+bn)
所有统计cut(n), sum(Σbi) powsum(所有bi的平方和),就可以维护了。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int mod=1e9+7;
int a[20];
LL pow10[20];
struct node
{
LL cut;
LL sum;
LL powsum;
int f;
node(){cut=0, sum=0, powsum=0, f=0;}
}dp[20][10][10];
node dfs(int pos, LL md, LL s, int mt)
{
if(pos==-1)
{
node now;
if(md==0||s==0)
{
now.f=1;
return now;
}
else
{
now.cut=1, now.sum=0, now.powsum=0, now.f=1;
return now;
}
}
if(!mt&&dp[pos][md][s].f)
{
return dp[pos][md][s];
}
int Len=mt?a[pos]:9;
node ans;
for(LL i=0;i<=Len;i++)
{
if(i==7)
{
continue;
}
node now=dfs(pos-1, (md*10+i)%7, (s+i)%7, mt&&i==a[pos]);
ans.cut=(ans.cut+now.cut)%mod, ans.sum=((ans.sum+(((i*pow10[pos])%mod)*now.cut)%mod)%mod+now.sum)%mod;
ans.powsum=(ans.powsum+(((i*pow10[pos]%mod)*(i*pow10[pos]%mod))%mod*now.cut)%mod+now.powsum+((2*((i*pow10[pos]%mod)*now.sum)%mod)%mod)%mod)%mod;
ans.f=1;
}
if(!mt)
{
dp[pos][md][s]=ans;
}
return ans;
}
LL slove(LL x)
{
int pos=0;
while(x)
{
a[pos++]=x%10;
x/=10;
}
return (dfs(pos-1, 0, 0, 1).powsum)%mod;
}
int main()
{
pow10[0]=1;
for(int i=1;i<20;i++)
{
pow10[i]=pow10[i-1]*10;
}
int T;
scanf("%d",&T);
while(T--)
{
LL L, R;
scanf("%lld%lld",&L,&R);
printf("%lld\n", (slove(R)-slove(L-1)+mod)%mod);
}
return 0;
}