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

7 篇文章 0 订阅

Title

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

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

3
1 9
10 11
17 17

Sample Output

236
221
0

题解:
一道……很毒瘤的数位dp。
我们可以定义一个结构体,用cnt表示符合要求的个数,sum表示这些数的和,sqsum表示平方和。
cnt很简单,一个朴素的数位dp。
sum可以用cnt表示。
这个数就是10^pos*i+x x表示这个数的后面部分。
则平方和就应该加
(10^pos*i+x)^2=(10^pos*i)^2+2*(10^pos*i*x+x^2

但是现在后面有一堆数。
即sum=10^pos*i+x1+10^pos*2+x2+…+10^pos*i+xn
=10^pos*i*cnt+(x1+x2+…+xn)
//cnt表示数的数量,而后面的一坨表示这一位的下一位的和。
同理,sqsum=(10^pos*i)^2*cnt+2*10^pos*i*(x1+x2+…+xn)+(x1^2+x2^2+…+性能……2)

用u表示之后的位的答案。
ans.sum=(10^pos*i)*u.cnt+u.sum;
ans.sqsum=(10^pos*i)^2*u.cnt+2*10^pos*i*u.sum+u.sqsum;

取模有毒。
而且我踏马一开始还看错了模数。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;

const int N = 20;
const int M = 10;
const int MOD = 1e9+7;

int t;
int a[N];
ll mpow[N];

struct node{
    ll cnt,sum,sqsum;

    node() {cnt=-1;sum=sqsum=0;}
    void unit(){
        cnt=sum=sqsum=0;
    }

}dp[N][M][M];

node dfs(int pos,int mod,int sum,bool zero,bool limit){
    if(pos==-1){
        node u;u.cnt=(mod&&sum);
        u.sum=0,u.sqsum=0;
        return u;
    }
    if(!zero&&!limit&&dp[pos][mod][sum].cnt!=-1)
        return dp[pos][mod][sum];
    int mx;if(limit) mx=a[pos];else mx=9;
    node ans;ans.unit();
    for(int i=0;i<=mx;i++){
        if(i==7) continue;
        node u=dfs(pos-1,(mod*10+i)%7,(sum+i)%7,zero&&i==0,limit&&i==mx);
        ans.cnt=(ans.cnt+u.cnt)%MOD;
        //ans.cnt%=MOD;
        ll a=mpow[pos];
        ans.sum+=(u.sum+((a*i)%MOD)*u.cnt%MOD)%MOD;
        ans.sum%=MOD;

        ans.sqsum+=(u.sqsum+((2*a*i)%MOD)*u.sum)%MOD;
        ans.sqsum%=MOD;
        ans.sqsum+=((u.cnt*a)%MOD*a%MOD*i*i%MOD);
        ans.sqsum%=MOD;

       /* ans.sum=((ans.sum+i*a%MOD)*u.cnt%MOD)%MOD;
        ans.sum=(ans.sum+u.sum)%MOD;

        ans.sqsum=(ans.sqsum+((a*i)%MOD*(a*i%MOD)*(u.cnt%MOD))%MOD);
        ans.sqsum=(ans.sqsum+(2*a*i%MOD*u.sum)%MOD)%MOD;
        ans.sqsum=(ans.sqsum+(u.sqsum%MOD))%MOD;*/

    }
    if(!limit&&!zero) dp[pos][mod][sum]=ans;
    return ans;
}

ll l,r;

ll getans(ll x){
    int cnt=0;
    while(x){
        a[cnt++]=x%10;
        x/=10;
    }
    return dfs(cnt-1,0,0,1,1).sqsum;
}

int main(){
    mpow[0]=1;
    for(int i=1;i<=18;i++) mpow[i]=(mpow[i-1]*10)%MOD;
    scanf("%d",&t);
    while(t--){
      cin>>l>>r;
      ll ans=getans(r);
      ans-=getans(l-1);
      cout<<(ans%MOD+MOD)%MOD<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值