[数位DP] HDU4507 吉哥系列故事——恨7不成妻

7 篇文章 0 订阅

fi,j,k,s 表示DP了前i位,数位和%7的余数为j,前i位的数字%7余数为k,前s位是否与上限相同 的方案数。
gi,j,k,s 一样,但表示的是所有情况的平方和。
hi,j,k,s 表示所有情况的和

三个数组同时转移就可以了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

typedef long long ll;

const int N=30,P=1e9+7;

int t;
ll l,r,pw[20];
int g[N][10][10][2],h[N][10][10][2],f[N][10][10][2];

inline void add(int &x,int y){
    if((x+=y)>=P) x-=P;
}

inline int calc(ll n){
    memset(g,0,sizeof(g));
    memset(h,0,sizeof(h));
    memset(f,0,sizeof(f));
    f[20][0][0][1]=1;
    for(int i=20;i>1;i--)
        for(int j=0;j<7;j++)
            for(int k=0;k<7;k++)
                for(int s=0;s<=1;s++)
                    if(s==0){
                        if(!f[i][j][k][s]) continue;
                        for(int d=0;d<=9;d++){
                            if(d==7) continue;
                            add(f[i-1][(j+d)%7][(k*10+d)%7][0],f[i][j][k][0]);
                            add(h[i-1][(j+d)%7][(k*10+d)%7][0],(100LL*h[i][j][k][0]+20LL*g[i][j][k][0]*d+1LL*f[i][j][k][0]*d*d)%P);
                            add(g[i-1][(j+d)%7][(k*10+d)%7][0],(10LL*g[i][j][k][0]+1LL*f[i][j][k][0]*d)%P);
                        }
                    }
                    else{
                        for(int d=0;d<(n/pw[i-1])%10;d++){
                            if(d==7) continue;
                            add(f[i-1][(j+d)%7][(k*10+d)%7][0],f[i][j][k][s]);
                            add(h[i-1][(j+d)%7][(k*10+d)%7][0],(100LL*h[i][j][k][s]+20LL*g[i][j][k][s]*d+1LL*f[i][j][k][s]*d*d)%P);
                            add(g[i-1][(j+d)%7][(k*10+d)%7][0],(10LL*g[i][j][k][s]+1LL*f[i][j][k][s]*d)%P);
                        }
                        int d=(n/pw[i-1])%10;
                        if(d==7) continue;
                        add(f[i-1][(j+d)%7][(k*10+d)%7][1],f[i][j][k][s]);
                        add(h[i-1][(j+d)%7][(k*10+d)%7][1],(100LL*h[i][j][k][s]+20LL*g[i][j][k][s]*d+1LL*f[i][j][k][s]*d*d)%P);
                        add(g[i-1][(j+d)%7][(k*10+d)%7][1],(10LL*g[i][j][k][s]+1LL*f[i][j][k][s]*d)%P);
                    }
    int ret=0;
    for(int i=1;i<7;i++)
        for(int j=1;j<7;j++)
            add(ret,(h[1][i][j][0]+h[1][i][j][1])%P);
    return ret;
}

int main(){
    scanf("%d",&t);
    pw[1]=1; for(int i=2;i<=19;i++) pw[i]=pw[i-1]*10;
    while(t--){
        scanf("%lld %lld\n",&l,&r);
        printf("%d\n",(calc(r)+P-calc(l-1))%P);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值