HDU 4507 数位DP

题意

问某个区间内与7无关的数的平方和%1e9+7的结果是多少。
与7无关即:不含7,可以被7整除,所有位加起来可以被7整除

题解

这道题不是一般的数位DP题,一般的数位DP题都是统计某个区间内有多少个这样的数。这种数位DP就很简单了,直接DFS记忆话搜索,过滤一些情况,就可以了。
这道题过滤情况没有什么难度,难点在于要求的是平方和,以及最后极其变态的MOD要求。
因为要求平方和,所以就必须要想办法重构平方和。平方和=(i*10^(len)+x)^2。(此处将一个数拆分为两部分,进行平方和)进行分解后,可以得到2*i*10^(len)*x*cnt(cnt为能够组成这个数的可能情况)+(i*10^(len))^2+sum(x)。由上述式子可以看出,还需要两个变量,sum(x)和cnt。
cnt就很简单了,就是一个区间内符合与7无关的数的个数。
至于sum(x)依然是分解成两部分进行求解,sum(x)=(i*10^(len)+x)。后面的x就是转移到这个状态之前的sum(x),这样的话sum(x)很容就能求得。

注意事项

这道题的难点一方面在于信息的记录与转移,另一方面就在于变态的数据范围。由于数据范围很大,随时可能超LongLong,因此需要及时MOD。

代码

#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 100010
#define EPS 1e-10
#define MOD 1000000007
#define int LL

using namespace std;


struct Node{
    int cnt,sum,sqsum;
    Node(){};
    Node(int c,int s,int q):cnt(c),sum(s),sqsum(q){}
};

int dig[20];
Node dp[20][10][10];
int c[20];
Node dfs(int len,int sum,int mod,bool up){
    if(len==0){
        if(mod!=0&&sum!=0)
            return Node(1,0,0);
        else
            return Node(0,0,0);
    }
    if(!up&&dp[len][sum][mod].cnt!=-1)
        return dp[len][sum][mod];
    int n=up?dig[len]:9;
    Node ans=Node(0,0,0);
    UP(i,0,n+1){
        if(i!=7){
            Node nd=dfs(len-1,(sum+i)%7,(mod*10+i)%7,up&&(i==n));
            ans.cnt=(ans.cnt+nd.cnt)%MOD;
            ans.sum=(ans.sum+nd.sum+((c[len-1]*nd.cnt)%MOD*i))%MOD;
            ans.sqsum=(ans.sqsum+nd.sqsum+((2*nd.sum*i)%MOD*c[len-1])%MOD+(((nd.cnt*i)%MOD*c[len-1])%MOD*i*c[len-1])%MOD)%MOD;
        }
    }
    if(!up){
        dp[len][sum][mod]=ans;
    }
    return ans;
}

Node solve(int x){
    int len=0;
    W(x>0){
        dig[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,0,true);
}

void init(){
    c[0]=1;
    UP(i,1,20){
        c[i]=(c[i-1]*10)%MOD;
    }
}

main(){
    int kase;
    scanf("%I64d",&kase);
    init();
    MEM(dp,-1);
    W(kase--){
        int a,b;
        scanf("%I64d%I64d",&a,&b);
        printf("%I64d\n",(solve(b).sqsum+MOD-solve(a-1).sqsum)%MOD);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值