HDU 4507 吉哥系列故事——恨7不成妻

取自洛谷题解
如果一个整数符合下面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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值