​http://acm.hdu.edu.cn/showproblem.php?pid=3652​

首先先解决怎么判断它是否含有13这个子串。

方法就类似于一个状态记录dp

加多一维[0 or 1]判断是否已经含有了13这个子串,那么如果枚举的时候,相邻的两位是13,则可由0跳转去1

 

这题是设dp[i][j][0 or 1][r]表示i位数,以j为开头的,是否含有13这个子串了,然后余数是r( % 13)

至于为什么要加上余数这个条件,

是因为在统计答案的过程中,统计答案的方法和上一篇类似。有些不同的就是需要用余数来判断这一位应该加上那些数字。

因为它是拆分开来的,按位dp

比如26XX,那么XX后面的余数应该是0,这样才能整除以13

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL;


#include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <bitset>
int n;
int dp[12][12][2][15];
LL base[15];
void init() {
base[0] = 1;
for (int i = 1; i <= 10; ++i) {
base[i] = base[i - 1] * 10;
}
for (int i = 0; i <= 9; ++i) {
dp[1][i][0][i] = 1;
}
for (int i = 2; i <= 10; ++i) { //枚举一共有i位
for (int j = 0; j <= 9; ++j) { //枚举第i位
for (int x = 0; x <= 9; ++x) { //第i - 1位
int t = base[i - 1] * j % 13;
for (int r = 0; r <= 12; ++r) {
dp[i][j][1][(r + t) % 13] += dp[i - 1][x][1][r];
if (j == 1 && x == 3) {
dp[i][j][1][(r + t) % 13] += dp[i - 1][x][0][r];
} else {
dp[i][j][0][(r + t) % 13] += dp[i - 1][x][0][r];
}
}
}
}
}
}
void work() {
n++;
int digit[15] = {0};
int lenstr = 0;
while (n / 10 > 0) {
digit[++lenstr] = n % 10;
n /= 10;
}
digit[++lenstr] = n % 10;
int ans = 0;
int mod = 0;
bool flag = false;
for (int i = lenstr; i >= 1; --i) {
for (int j = 0; j <= digit[i] - 1; ++j) {
ans += dp[i][j][1][(13 - mod) % 13];
if (flag || j == 3 && digit[i + 1] == 1) {
ans += dp[i][j][0][(13 - mod) % 13];
}
}
if (digit[i] == 3 && digit[i + 1] == 1) {
flag = true;
}
mod = (mod + digit[i] * base[i - 1]) % 13;
}
cout << ans << endl;
}
int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
init();
while (cin >> n) work();
return 0;
}

View Code