恨 7不成妻

题目描述

什么样的数和 7 有关呢?如果一个整数符合下面三个条件之一,那么我们就说这个整数和 7 有关:
一. 整数中某一位是 7;
二. 整数的每一位加起来的和是 7 的整数倍;
三. 这个整数是 7 的整数倍。
现在问题来了:吉哥想知道在一定区间内和 7 无关的数字的平方和。

输入

输入数据的第一行是测试数据组数 T,然后接下来的 T 行表示 T 组测试数据。
每组数据在一行内包含两个正整数 L, R。

输出

对于每组数据,请计算 [L,R] 中和 7 无关的数字的平方和,并将结果对 \(10 ^ 9 +7\) 取模后输出。

样例输入

3
1 9
10 11
17 17

样例输出

236
221
0

思路

用记搜解决。

第一点要求“整数中某一位是 7”,那么搜索过程中遇到7就剪枝;
第二点要求“整数的每一位加起来的和是 7 的整数倍”,则在搜索时记录前面各位数字之和%7的结果;
第三点要求“ 这个整数是 7 的整数倍”,则在搜索时记录前面的数%7的结果;

重点是答案要求的是平方和。

举个例子,假设求100~123的平方和

\[\begin{aligned} &(100)^2+(101)^2+...+(123)^2 \\ =&(100)^2+(100+1)^2+...+(100+23)^2\\ =&24*100^2+2*100*(0+1+...+23)+0^2+1^2+...+23^2 \end{aligned} \]

因此,我们要想得到答案需要知道3个数:

要加的项数cnt\((上式中24)\)
下一层所有项的和sum1\((0+1+...+23)\)
下一层所有项的平方和sum2\((0^2+1^2+...+23^2)\)

因此需开一个结构体维护这三个数
再来看看如何从下一层的这三个数转移到该层
cnt:很简单,只需要每次把下层的累加上来就行了
sum1:可能有人会认为也只需累加就行,但其实不然,累加上来的只是(0+1+...+23),而我们想要的是(100+101+...+123)。所以我们需要在累加后再补上缺少的cnt个100
sum2:根据上面推出的式子,sum2应该\(=cnt*100^2+2*100*sum1+(下一层的)sum2\)

\(更普遍地,只需把上面的100换成当前枚举的数字i*10^{len}即可(len为当前长度)\)

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int digit[20];
const long long mm=1e9+7;
long long po[30];
struct node
{
    long long cnt,sum,squsum;
    node()
    {
        cnt=sum=squsum=0;
    }
};
node dp[20][20][20];
bool ok[20][20][20]={0};
node dfs(int len,int gsum,int mod,bool fp)//len:当前数的长度;gsum:前面各位数字之和%7的结果;mod:前面的数%7的结果;fp:是否到达顶端
{
    node ren;
    if (!len)
    {
        if(gsum&&mod)ren.cnt=1;//只有该数满足性质时才计数
        return ren;
    }
    if (!fp&&ok[len][gsum][mod])return dp[len][gsum][mod];//如果已经搜过就直接返回结果
    int fpmax=fp?digit[len]:9;
    node ret,cinn;
    for (int i=0;i<=fpmax;i++)
    {
        if(i==7)continue;
        cinn=dfs(len-1,(gsum+i)%7,(mod*10+i)%7,fp&&i==fpmax);
        ret.cnt=(ret.cnt+cinn.cnt)%mm;//累加cnt
        ret.sum=((ret.sum+cinn.sum)%mm+((cinn.cnt*i)%mm*po[len])%mm)%mm;//转移sum1
        ret.squsum=(ret.squsum+cinn.squsum)%mm;//转移sum2
        ret.squsum=(ret.squsum+(cinn.cnt*((po[len]*po[len])%mm*(i*i)%mm)%mm)%mm)%mm;
        ret.squsum=(ret.squsum+(2*(cinn.sum*po[len])%mm*i)%mm)%mm;
    }
    if(!fp){ok[len][gsum][mod]=1;dp[len][gsum][mod]=ret;}//记忆
    return ret;
}
int f(int n)
{
    int len = 0;
    while(n)
    {
        digit[++len] = n % 10;
        n /= 10;
    }
    return (dfs(len,0,0,1).squsum)%mm;
}
signed main()
{
    int a,b,n;
    memset(dp,-1,sizeof(dp));
    po[1]=1;
    for(int i=2;i<=18;i++)
        po[i]=(po[i-1]*10)%mm;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&a,&b);
        printf("%lld\n",(f(b)-f(a-1)+mm)%mm);
    }
    return 0;
}
 posted on 2022-06-27 18:06  hu_led  阅读(44)  评论(1)    收藏  举报