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

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


对于第一个条件:在DP中多开一维0/1表示前面的位数中是否出现1
对于第二个条件:在DP中多开一维表示前面位数的和对7取余
对于第三个条件:在DP中多开一维表示生成的这个数对7取余

那么考虑一下边界:
如果出现7并且两个取余都是0,那么这个数就是跟7相关。
然后再来维护一下平方和。
这里用一个sum来维护,具体公式就不推了很好推的。
还要维护个数和平方和,那么就开一个结构体来DP;
在进行维护数据时要注意公式的正确性和取模。

#include<stdio.h>
#include<algorithm>
#include<string>
#include<string.h>
#include<queue>
#include<vector>
#include<stack>
#include<math.h>
#include<map>
#include<set>
#include<iostream>
using namespace std;
#define INF 0x7f7f7f7f
#define LL long long
const LL mod=1e9+7;
struct node
{
    LL cnt;
    LL sum;
    LL sum2;
}dp[20][2][10][10];
int a[20];
LL l,r;
LL q(int x)
{
    LL tmp=1;
    for(int i=1;i<=x;i++)
    {
        tmp*=10;
        tmp%=mod;
    }
    return tmp;
}
node dfs(int pos,int pre,int sta,int sa,int sb,bool limit)
{
    if(pos==-1)
    {
        node tmp;
        if(sta==1||sa%7==0||sb%7==0)
        {
            tmp.cnt=0;
            tmp.sum=0;
            tmp.sum2=0;
            return tmp;
        }
        else
        {
            tmp.cnt=1;
            tmp.sum=0;
            tmp.sum2=0;
            return tmp;
        }
    }
    if(!limit&&dp[pos][sta][sa][sb].cnt!=-1)
        return dp[pos][sta][sa][sb];
    int up=limit ? a[pos] : 9;
    node tmp;
    tmp.cnt=tmp.sum=tmp.sum2=0;
    for(int i=0;i<=up;i++)
    {
        node ttp=dfs(pos-1,i,sta||i==7,(sa+i)%7,(sb*10+i)%7,limit&&i==a[pos]);
        if(ttp.cnt>=1)
        {
            LL x=i*q(pos);
            x%=mod;

            tmp.cnt+=ttp.cnt;
            tmp.cnt%=mod;

            if(pos==0)
                tmp.sum+=i;
            else
                tmp.sum+=ttp.sum+x*ttp.cnt%mod;
            tmp.sum%=mod;

            tmp.sum2+=(((x*x)%mod)*ttp.cnt)%mod+((2*x)%mod)*ttp.sum%mod+ttp.sum2%mod;
            tmp.sum2%=mod;

        }
    }
    if(!limit)
        dp[pos][sta][sa][sb]=tmp;
    return tmp;
}
node solve(LL x)
{
    int pos=0;
    while(x)
    {
        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,-1,0,0,0,true);
}
int main()
{
    int t;
    for(int i=0;i<20;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<10;k++)
                for(int kk=0;kk<10;kk++)
                    dp[i][j][k][kk].cnt=-1;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%I64d%I64d",&l,&r);
        LL ans=(solve(r).sum2-solve(l-1).sum2);
        if(ans<0)
            ans+=mod;
        printf("%I64d\n",ans);
    }
return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值