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

题目链接
题意

求一定区间内和7无关的数字的平方和。

思路

数位DP求以下三种东西
设数位DP当前位为now,下次搜索为i,后缀位为nt

  • cnt 与7无关数的个数,基础数位DP
  • sum 与7无关数的和,now.sum = (i*10^pos)*nt.cnt+nt.sum
  • q 与7无关数的平方和, n o w . q = ( i ∗ 1 0 p o s + n t . s u m ) 2 = i ∗ i ∗ 1 0 p o s ∗ 1 0 p o s + 2 ∗ i ∗ 1 0 p o s ∗ n t . s u m + n t . q now.q = (i*10^{pos}+nt.sum) ^2 = i*i*10 ^{pos}*10 ^{pos}+2*i*10 ^{pos}*nt.sum+nt.q now.q=(i10pos+nt.sum)2=ii10pos10pos+2i10posnt.sum+nt.q

i ∗ i ∗ 1 0 p o s ∗ 1 0 p o s i*i*10 ^{pos}*10 ^{pos} ii10pos10pos出现nt.cnt次需要额外×nt.cnt
2 ∗ i ∗ 1 0 p o s ∗ n t . s u m 2*i*10 ^{pos}*nt.sum 2i10posnt.sum 无需×nt.cnt,因为nt.sum已经将当前搜索i的后缀的全部求和
n t . q = n t . s u m ∗ n t . s u m nt.q = nt.sum*nt.sum nt.q=nt.sumnt.sum直接继承即可

代码
#include <stdio.h>
#include <string.h>

#define ll long long

struct Node
{
    ll cnt, sum, q, f;
    Node():cnt(0),sum(0),q(0),f(0){}
}dp[20][10][10];

ll num[20], mod = 1e9+7, de[20];

Node dfs(ll pos, ll limit, ll sum, ll now)
{
    if(pos == -1)
    {
        Node nt;
        if(sum && now) nt.cnt = 1;
        return nt;
    }
    if(!limit && dp[pos][sum][now].f) return dp[pos][sum][now];
    ll up = limit ? num[pos] : 9;
    Node nt, tmp;
    for(ll i = 0; i <= up; ++i)
    {
        if(i == 7) continue;
        nt = dfs(pos-1, limit&&i==up, (sum*10+i)%7, (now+i)%7);
        tmp.cnt += nt.cnt;
        tmp.cnt %= mod;

        tmp.sum += (nt.cnt*i%mod*de[pos]%mod+nt.sum)%mod;
        tmp.sum %= mod;

        tmp.q += (nt.q+2*nt.sum*(i*de[pos]%mod)%mod+(i*de[pos]%mod)*(i*de[pos]%mod)%mod*nt.cnt%mod)%mod;
        tmp.q %= mod;
    }
    tmp.f = 1;
    return limit ? tmp : dp[pos][sum][now] = tmp;
}

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

int main()
{
    ll t, l, r;
    memset(dp,0,sizeof(dp));
    de[0] = 1;
    for(ll i = 1; i <= 20; ++i) de[i] = 10*de[i-1]%mod;
    for(scanf("%lld",&t); t; --t)
    {
        scanf("%lld%lld",&l,&r);
        printf("%lld\n",(solve(r)-solve(l-1)+mod)%mod);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值