[BZOJ5064] [HDU3652] B-number 数位DP

题目描述

B数的定义:能被13整除且本身包含字符串"13"的数。

例如:130和2613是B数,但是143和2639不是B数。

你的任务是计算1到n之间有多少个数是B数。

输入

输入数据只有一个数,为n。(\(1<=N<=10^{15}\)

输出

输出数据包含一行,为1到n之间B数的个数。

样例输入

13

样例输出

1

题解

数位DP

记录对13的模数来判断是否可以被13整除。

同时记录是否出现过‘13’, 还要考虑首位前导0的情况。

\(f[i][j][k][0/1]\)\(i\)位数,对\(13\)取模结果为\(j\),首位为\(k\),是否包含\(13\)的数的个数。

预处理\(f\)数组后进行数位DP。

先填充位数不满的,再由高位向低位将此位不满的加入答案。

转换询问区间为\([1,n)\)则更易简单处理。

code

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
LL n, b[17], f[17][13][10][2];
void init() {
    b[0] = f[0][0][0][0] = 1;
    for(int i = 1;i <= 16;i ++) {
        b[i] = b[i-1] * 10;
        for(int j = 0;j < 13;j ++) {
            for(int k = 0;k <= 9;k ++) {
                for(int l = 0;l <= 9;l ++) {
                    f[i][(j+k*b[i-1])%13][k][1||(k==1&&l==3)] += f[i-1][j][l][1];
                    f[i][(j+k*b[i-1])%13][k][0||(k==1&&l==3)] += f[i-1][j][l][0];
                }
            }
        }
    }
}

int main() {
    init();
    while(cin >> n) {
        int x = n + 1;
        int pos , j, di = 1 , flag = 0;
        LL now = 0 , ans = 0;
        for(pos = 1 ; b[pos] <= x ; pos ++ )
               for(j = 1 ; j < 10 ; j ++ )
                ans += f[pos][0][j][1];
        for( ; pos ; pos -- ) {
            for(j = di ; j < x / b[pos - 1] % 10 ; j ++ )
                ans += f[pos][(13 - now * b[pos] % 13) % 13][j][1] + (flag || (x / b[pos] % 10 == 1 && j == 3)) * f[pos][(13 - now * b[pos] % 13) % 13][j][0];
            now = (now * 10 + x / b[pos - 1] % 10) % 13 , di = 0;
            if(x / b[pos] % 10 == 1 && x / b[pos - 1] % 10 == 3) flag = 1;
        }
        cout << ans << endl;
    }
    return 0;
}
posted @ 2019-08-13 06:45  Paranoid丶离殇  阅读(159)  评论(0编辑  收藏  举报