【数位dp && 数学技巧】HDU - 4507 吉哥系列故事――恨7不成妻

Problem Description

如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是7;
  2、整数的每一位加起来的和是7的整数倍;
  3、这个整数是7的整数倍;
  现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。

思路:

参考博客:https://www.cnblogs.com/kuangbin/archive/2013/05/01/3053233.html
要求一个区间中和7无关的数的平方和。
需要用数位DP维护3个值:
1.与7无关的数的个数
2.与7无关的数的和
3、与7无关的数的平方和。
第一个 与7无关的数的个数,很好求。
第二个 1-25的和 = 20*6 +0+1+2+3+4+5 + 10*10 + (0+1+2+3+4+5+6+7+8+9)*2 用到这样的思想
第三个 23的平方 = (20 + 3)^2 = 20^2 + 2*20*3 + 3^2; 20-23的平方和 = (20 + 1)^2 + (20 + 2)^2 + (20 + 3)^2 = 3*20^2 + 2*20*(1+2+3) + 1^2 + 2^2 + 3^2; 用到这样的思想

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MOD = 1e9+7;
struct node//核心点就是维护这三个变量
{
    ll num, sum, sqsum;
};
node dp[25][10][10];
ll a[25], z[25];
node dfs(int pos, ll dsum, ll dnum, int limit)
{
    if(pos == -1) {
        node temp;
        temp.num = dsum && dnum;
        temp.sum = temp.sqsum = 0;
        return temp;
    }
    if(!limit && dp[pos][dsum][dnum].num != -1) return dp[pos][dsum][dnum];
    int up = limit ? a[pos] : 9;
    node res;
    res.num = res.sqsum = res.sum = 0;
    for(int i = 0; i <= up; i++)
    {
        if(i == 7) continue;
        node temp = dfs(pos-1, (dsum+i)%7, (dnum*10+i)%7, limit && i == a[pos]);
        //核心点
        res.num = (res.num + temp.num)%MOD;

        res.sum += (((i*z[pos])%MOD*temp.num)%MOD + temp.sum)%MOD;
        res.sum %= MOD;

        res.sqsum += (((2*i*z[pos])%MOD)*temp.sum)%MOD;
        res.sqsum %= MOD;
        res.sqsum += (((((i*z[pos])%MOD * (i*z[pos])%MOD))%MOD * temp.num)%MOD + temp.sqsum)%MOD;
        res.sqsum %= MOD;
        //
    }
    if(!limit) return dp[pos][dsum][dnum] = res;
    else return res;
}
ll solve(ll x)
{
    int pos = 0;
    while(x)
    {
        a[pos++] = x%10;
        x /= 10;
    }
    return dfs(pos-1, 0, 0, 1).sqsum;
}
void init()
{
    for(int i = 0; i <= 20; i++)
    {
        for(int j = 0; j <= 8; j++)
        {
            for(int k = 0; k <= 8; k++)
                dp[i][j][k].num = -1;
        }
    }
    z[0] = 1;
    for(int i = 1; i <= 18; i++)
        z[i] = (z[i-1]*10)%MOD;
}
int main()
{
    int T;
    scanf("%d", &T);
    ll lt, rt;
    init();
    while(T--)
    {
        cin >> lt >> rt;
        cout << (solve(rt) - solve(lt-1) + MOD)%MOD << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值