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; }