HDU 4507 吉哥系列故事——恨7不成妻 (比较繁琐的数位DP)

题目链接 :http://acm.hdu.edu.cn/showproblem.php?pid=4507

题意 :找出[l, r]之间所有不满足以下任意一条的数的平方之和 :

    1、整数中某一位是7;
    2、整数的每一位加起来的和是7的整数倍;
    3、这个整数是7的整数倍;

思路 : 数位DP, 繁琐的是处理平方和,可以这样考虑当你算到len位的时候获得的数字是x,然后下一位放进去的数字是i,那么其实所获得的新的值应该是x + i * 10 ^ (len-1), 然后要计算它的平方, 那么展开后就是  x * x + f * f + 2 * f * x ;(f = i * 10 ^ (len - 1)),这样的话需要统计在前继状态中获得的所有满足条件的数的个数cnt, 所有满足条件的数的和s1, 所有满足条件的数的平方和s2.然后用GYZ的数位DP写法秒了, 注意下别溢出即可。

PS : 一开始打算是把当前算到的位置所获得数字平方和传进去, 但是空间更本就是开不下的。看了爱神(cxlove)的blog才发现原来可以倒的把数据返回来, 这样就不需要担心爆内存了。


#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      

using namespace std;
typedef __int64 lld;
const int mod = 1e9 + 7;

int bit[20];
lld cnt[20][2][7][7], s1[20][2][7][7], s2[20][2][7][7];
lld Fac[20];

struct Bo{
    lld cnt, s1, s2;
    Bo(){}
    Bo(lld a, lld b, lld c) : cnt(a), s1(b), s2(c){}
};

Bo dfs(int len, int a, int b, int c, int fp){
    if (!fp && cnt[len][a][b][c] != (lld)-1){
        return Bo(cnt[len][a][b][c], s1[len][a][b][c], s2[len][a][b][c]);
    }
    if (!len){
        if (!a && b && c){
            cnt[len][a][b][c] = 1;
        }else {
            cnt[len][a][b][c] = 0;
        }
        s1[len][a][b][c] = s2[len][a][b][c] = 0;
        return Bo(cnt[len][a][b][c], s1[len][a][b][c], s2[len][a][b][c]);
    }
    int Max = (fp ? bit[len] : 9);
    lld vc = 0, vs1 = 0, vs2 = 0;
    for (int i = 0; i <= Max; i++){
        int nl = len - 1 , na = (a || i == 7), nb = (b + i) % 7, nc = (c * 10 + i) % 7;
        Bo tmp = dfs(nl, na, nb, nc, i == Max && fp);
        lld f = Fac[nl] * i % mod;
        vc = (vc + tmp.cnt) % mod;
        vs1 = (vs1 + tmp.s1 + tmp.cnt * f) % mod;
        vs2 = (vs2 + tmp.s2 + (f * f) % mod * tmp.cnt + 2 * f * tmp.s1) % mod;
    }
    if (!fp){
        cnt[len][a][b][c] = vc;
        s1[len][a][b][c] = vs1;
        s2[len][a][b][c] = vs2;
    }
    return Bo(vc, vs1, vs2);
}

lld solve(lld x){
    lld tmp = x;
    int len = 0;
    while (tmp){
        bit[++len] = tmp % 10;
        tmp /= 10;
    }
    Bo res = dfs(len, 0, 0, 0, 1);
    return res.s2 % mod;
}

void init(){
    Fac[0] = 1;
    for (int i = 1; i <= 18; i++){
        Fac[i] = Fac[i-1] * 10 % mod;
    }
    memset(cnt, -1, sizeof(cnt));
}

int main(){
    int T;
    scanf("%d", &T); init();
    for (int cas = 1; cas <= T; cas++){
        lld l, r;
        scanf("%I64d%I64d", &l, &r);
        printf("%I64d\n", (solve(r) - solve(l - 1) + mod) % mod);
    }
    return 0;
}

     
     
    
    
   
   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值