[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;
}
作者:Paranoid丶离殇
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。