hdu4507——数位dp(计数求和)

题目链接:https://vjudge.net/problem/HDU-4507

题目大意:

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

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

题解:

这个题约束条件很常见,但是要求的不是数的个数,而是平方和就有点恶心了。

首先考虑求数的个数

定义状态dp[pos][sum1][sum2]表示枚举到pos位,前面数位的和为sum1,前面数位形成的数对7取余的结果sum2.

最后的时候再判断sum1%7!=0并且sum2!=0就行了。

然后考虑求满足条件的数的和。

当枚举到pos位时,我们用ans累加的不再是后面数位满足条件的数的个数,而是后面数位的个数*i*10^pos+后面数位形成的数,相当于给后面数位的每个数都分配了i*10^pos。

最后考虑数的平方和。

用pre表示当前数位,next表示后面数位

(pre*10^pos + next)^2= (pre*10^pos)^2+2*pre*10^pos*next +next^2

因此我们需要算出(pre*10^pos)^2和2*pre*10^pos*(后面数位的和)+后面数位的平方和(递归得到)

e.g:       

假设满足条件的数有234,245,266,

那么 234^2 + 245^2 + 266^2

        = (200 + 34)^2 + (200 + 45)^2 + (200 + 66)^2 = 3*200^2 + 2*200*(34+45+66) + (34^2 + 35^2 + 66^2),

因此在枚举到2的时候,表达式里只有3,(34 + 45 + 66),(34^2 + 35^2 + 66^2)不知道

 

代码实现:

#pragma GCC optimize(2)
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#define PI atan(1.0) * 4
#define E 2.718281828
#define rp(i, s, t) for (register int i = (s); i <= (t); i++)
#define RP(i, t, s) for (register int i = (t); i >= (s); i--)
#define ll long long
#define ull unsigned long long
#define mst(a, b) memset(a, b, sizeof(a))
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define pii pair<int, int>
#define mp make_pair
#define pb push_back
#define debug printf("ac\n");
using namespace std;
inline int read()
{
    int a = 0, b = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-')
            b = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
    {
        a = (a << 3) + (a << 1) + c - '0';
        c = getchar();
    }
    return a * b;
}
const ll mod = 1e9+7;
int a[20],num;
ll fac[20];//预处理10的幂次
struct node{
    ll cnt;//与7无关的数的个数
    ll sum;//与7无关的数的和
    ll sqr;//与7无关的数的平方和。
    node(int c=0,int s=0,int ss=0):cnt(c),sum(s),sqr(ss){}
    // node():{}
}dp[20][20][20];
node dfs(int pos,int pre1,int pre2,int lead,int limit){
    if(pos==-1) return node(pre1!=0&&pre2!=0);
    if(!lead&&!limit&&dp[pos][pre1][pre2].cnt!=-1) return dp[pos][pre1][pre2];
    int up=limit?a[pos]:9;
    node ans;
    rp(i,0,up) {
        if(i==7) continue;
        node tmp=dfs(pos-1,(pre1+i)%7,(pre2*10+i)%7,lead&&i==0,limit&&i==a[pos]);
        //简单数位dp求与7无关的数的个数
        ans.cnt=(tmp.cnt+ans.cnt)%mod;
        //求与7无关的数的和
        //处理到第pos个数位时,加上i*10^pos * 后面的个数
        ans.sum=(ans.sum+tmp.sum+tmp.cnt*fac[pos]%mod*i%mod)%mod;//相当于给后面数位的每个数都加上一个i*10^pos
        //求与7无关的数的平方和
        //(pre*10^pos + next)^2= (pre*10^pos)^2+2*pre*10^pos*next +next^2
        ans.sqr=(ans.sqr+tmp.sqr+2*i%mod*fac[pos]%mod*tmp.sum%mod)%mod;
        ans.sqr=(ans.sqr+(tmp.cnt*fac[pos]%mod*fac[pos]%mod*i%mod*i%mod))%mod;
    }
    if(!limit&&!lead) return dp[pos][pre1][pre2]=ans;
    return ans;
}
ll solve(ll x){
    num=0;
    while(x) a[num++]=x%10,x/=10;
    return dfs(num-1,0,0,1,1).sqr;
}
int main(){
    mst(dp,-1);
    int T=read();
    ll l,r;
    fac[0]=1;
    rp(i,1,19) fac[i]=fac[i-1]*10%mod;
    rp(i,0,19) rp(j,0,19) rp(k,0,19) dp[i][j][k].cnt=-1;
    while(T--){
        scanf("%I64d%I64d",&l,&r);
        printf("%I64d\n",((solve(r)-solve(l-1))%mod+mod)%mod);
    }
    return 0;
}
/*
1 1000000000000000000
968811219
1 100000000000000000
476468678
*/

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值