数位DPhdu4507

题目如下
求[l , r] 中
 如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是7;
  2、整数的每一位加起来的和是7的整数倍;
  3、这个整数是7的整数倍;
吉哥想知道在一定区间内和7无关的数字的平方和,区间范围在1-10^18.
根据题意我们的状态为dp[i][j][k],表示之前的整数和对7的余数为j,之前的整数对7的余数为k,在第i位与7无关的数一共有多少个。
然后问题就是平方和的求解,假设数位dxxxxxxx,设当前位d的10次幂为x,后面的一串为y,那么平方和就为(x+y)^2=x^2+2*x*y+y^2。x^2就直接x^2乘以后面出现的次数,2*x*y为2*x*后面出现的每一位和。y^2即为下一层深度得到的值。代码如下

#include<stdio.h>
#include<string.h>
const int mod=1e9+7;
typedef long long ll;

int lbit[20],hbit[20];
ll ten[20];
struct node
{
    ll sum,num,sqr;
    bool flag;
    node(ll _num,ll _sum,ll _sqr):num(_num),sum(_sum),sqr(_sqr){}    
    node():flag(false){}
}dp[20][7][7];

int max(int a,int b)
{
    return a>b? a:b;
}

node dfs(int len,int sum7,int num7,bool lb,bool hb)
{
    if(len==0)
    {
        if(sum7!=0&&num7!=0) return node(1,0,0);
        else return node(0,0,0);
    }
    if(!lb&&!hb&&dp[len][sum7][num7].flag) 
    return dp[len][sum7][num7];
    int sta=lb? lbit[len]:0;
    int end=hb? hbit[len]:9;
    node ret(0,0,0),tmp;
//    printf("%d %d %d %d %d\n",len,sum7,num7,sta,end);
    for(int i=sta;i<=end;i++)
    {
        if(i==7) continue;
        tmp=dfs(len-1,(sum7+i)%7,(num7*10+i)%7,lb&&i==sta,hb&&i==end);
//        printf("!%d %d %d %lld %lld %lld %lld %l10ld %lld\n",len,i,end,tmp.num,tmp.sum,tmp.sqr,ret.num,ret.sum,ret.sqr);
        ret.num=(ret.num+tmp.num)%mod;
        ret.sum=(ret.sum+i*ten[len-1]%mod*tmp.num%mod+tmp.sum)%mod;
        ret.sqr=(ret.sqr+i*ten[len-1]%mod*i%mod*ten[len-1]%mod*tmp.num%mod+tmp.sqr+2*i*ten[len-1]%mod*tmp.sum%mod)%mod;
        //    printf("%d %d %d %lld %lld %lld %lld %lld %lld\n",len,i,end,tmp.num,tmp.sum,tmp.sqr,ret.num,ret.sum,ret.sqr);
    }
    if(!lb&&!hb)
    {
        dp[len][sum7][num7]=ret;
        dp[len][sum7][num7].flag=true;
    }
    return ret;
}

ll solve(ll l,ll r)
{
    memset(lbit,0,sizeof(lbit));
    memset(hbit,0,sizeof(hbit));
    int lidx=1,hidx=1;
    while(l)
    {
        lbit[lidx++]=l%10;
        l/=10;
    }
    while(r)
    {
        hbit[hidx++]=r%10;
        r/=10;
    }
    return dfs(max(lidx-1,hidx-1),0,0,true,true).sqr;
}
int main()
{
    int T;
    ten[0]=1;
    for(int i=1;i<20;i++)
    ten[i]=(ten[i-1]*10)%mod;
    scanf("%d",&T);
    while(T--)
    {
        ll l,r;;
        scanf("%lld %lld",&l,&r);
        printf("%lld\n",solve(l,r));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值