HDU - 4507 吉哥系列故事——恨7不成妻(数位DP)

本文介绍了一个有趣的编程挑战,通过数位DP算法解决寻找特定区间内与数字7无关的数字平方和的问题。文章详细解释了与7无关数字的定义及如何通过动态规划算法高效地求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

Time Limit: 1000/500 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 5721    Accepted Submission(s): 1864


Problem Description
  单身!
  依然单身!
  吉哥依然单身!
  DS级码农吉哥依然单身!
  所以,他生平最恨情人节,不管是214还是77,他都讨厌!
  
  吉哥观察了214和77这两个数,发现:
  2+1+4=7
  7+7=7*2
  77=7*11
  最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!

  什么样的数和7有关呢?

  如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是7;
  2、整数的每一位加起来的和是7的整数倍;
  3、这个整数是7的整数倍;

  现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
 

Input
输入数据的第一行是case数T(1 <= T <= 50),然后接下来的T行表示T个case;每个case在一行内包含两个正整数L, R(1 <= L <= R <= 10^18)。
 

Output
请计算[L,R]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。
 

Sample Input

     
31 910 1117 17
 
Sample Output

     
2362210
 

Source
 

Recommend
liuyiding
 

/**
题意相对来说比较清晰明了 寻找和7无关的数的平方和;
与7有关的定义:
<1>整数某一位是7
<2>整数每一位加起来和是7的倍数
<3>这个数是7的倍数
ans:区间内和7无关数的平方和

这个题十分的邪乎 求解的并不是满足条件的个数 而是算出平方的和
因此DP的时候需要维护三个值并用结构体进行存储
<1>符合条件的数的个数 cnt;
<2>符合条件数的和 sum;
<3>符合条件数的平方和 sqsum

由dfs推出返回的是next,当前结果的结构体是ans;

第一个是与7无关的数的个数,就是简单的数位DP了,很常规。

第二个与7无关的数的和的维护需要用到第一个个数
处理到第len个数位时,加上i*10^ pos * 后面的个数
简化为式子就是 next.sum+(10^len*i)*ans.cnt ;


第三个的维护需要用到前面两个
(i*10^pos + x)^2= (i*10^len)^2+2*i*10^len*x +x^2;
x为当前数的后半部分 
类似于  1024  被拆分为1000+24  然后去平方这样子....

最后 整体还需要乘以next.cnt 由于后续记忆化的过程并不止出现一个

因此:ans.sqsum+=next.sqsum

ans.sqsum+=(2*10^len*i*x)*next.cnt=(2*10^len*i)*next.sum 为了化简设立第二个变量sum 

ans.sqsum+=(10^len*i)^2*next.cnt

嗯嗯 大概就是这个样子了  最后把框架架好 注意取模就差不多了

*/

#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;

const int maxn=20+7;
const int mod=1e9+7;

struct node {
    ll cnt;//与7无关的数的个数
    ll sum;//与7无关的数和
    ll sqsum;//平方和
}dp[maxn][maxn][maxn];//数位len 数字和%7 数字%7

int num[maxn];
ll fac[maxn];

node dfs(int len,int digit,int pre,bool end_flag){
    if(len==-1){
        node tmp;
        tmp.cnt=(digit&&pre);
        tmp.sum=tmp.sqsum=0;
        return tmp;
    }
    if(!end_flag&&dp[len][digit][pre].cnt!=-1) return  dp[len][digit][pre];
    int maxx=end_flag?num[len]:9;
    node next,tmp;
    next.cnt=next.sqsum=next.sum=0;
    for(int i=0;i<=maxx;i++){
        if(i==7) continue;
        tmp=dfs(len-1,(digit+i)%7,(pre*10+i)%7,end_flag&&i==maxx);

        next.cnt+=tmp.cnt;
        next.cnt%=mod;

        next.sum+=(tmp.sum+(fac[len]*i%mod)*tmp.cnt%mod)%mod;
        next.sum%=mod;

        next.sqsum+=(tmp.sqsum+((2*fac[len]*i)%mod)*tmp.sum)%mod;
        next.sqsum%=mod;
        next.sqsum+=((tmp.cnt*fac[len]))%mod*fac[len]%mod*i*i%mod;
        next.sqsum%=mod;
    }
    if(!end_flag) dp[len][digit][pre]=next;
    return next;

}

ll solve(ll n){
    int len=0;
    while(n){
        num[len++]=n%10;
        n/=10;
    }
    return dfs(len-1,0,0,1).sqsum;
}

void init(){
    fac[0]=1;
    for(int i=1;i<=20;i++) fac[i]=fac[i-1]*10%mod;
    for(int i=0;i<maxn-1;i++)
        for(int j=0;j<maxn-1;j++)
        for(int k=0;k<maxn-1;k++)
        dp[i][j][k].cnt=-1;
}

int main (){
    init();
    int t;scanf("%d",&t);
    ll l,r;
    while(t--){
        scanf("%I64d %I64d",&l,&r);
        ll ans=solve(r)-solve(l-1);
        ans=(ans+mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值