数位dp。
有一种数,能被“13”整除而且数位里包含“13”,求[1,n]
中这种数的个数。
for short
是简称的意思。
之前求过数位里不包含“XX”的,中途判断就可以了。但如果求的是包含的个数,要么取反、要么只能最后判断。
以下几点。
- 这道题要用三维状态,两个条件各用一维。这两个条件都是运行到最后一位(反正大概是最后)才能判断出来。
- 对包含“13”的判断,设计三个状态,分别表示:
(0)之前还没出现过“13”且前一位不是“1”;
(1)之前还没出现过“13”且前一位是“1”;
(2)之前已出现过“13”。 - 对被“13”整除的判断,将
(
∑
n
=
i
+
1
p
o
s
−
1
a
n
⋅
1
0
n
)
 
m
o
d
 
13
(\sum_{n=i+1}^{pos-1}{a_{n}\cdot10^{n}})\bmod13
(n=i+1∑pos−1an⋅10n)mod13作为状态值。
其实是用了一个公式: ( a + b )   m o d   c = [ ( a   m o d   c ) + ( b   m o d   c ) ]   m o d   c (a+b)\bmod c = \Big[(a\bmod c)+(b\bmod c)\Big]\bmod c (a+b)modc=[(amodc)+(bmodc)]modc
最后判断是不是0
就完事了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
int n;
int dp[10][3][13]; // 牛批,没看题解一次写对
int num[10];
int dfs(int pos, int status, int remainder, bool limit)
{
if (pos == -1) return ((status == 2) && (remainder == 0)) ? 1 : 0; // 最后才能判断
if ((!limit) && (dp[pos][status][remainder] != -1)) return dp[pos][status][remainder];
int cnt = 0;
int up = (limit ? num[pos] : 9);
for (int i = 0; i <= up; i++)
{
int new_status;
if ((status == 2) || ((status == 1) && (i == 3))) new_status = 2;
else if (i == 1) new_status = 1;
else new_status = 0;
cnt += dfs(pos - 1, new_status, (remainder + (i * (int)pow(10, pos))) % 13, limit && (i == up));
}
if (!limit) dp[pos][status][remainder] = cnt;
return cnt;
}
int solve(int x)
{
int pos = 0;
for (; x;)
{
num[pos++] = x % 10;
x /= 10;
}
return dfs(pos - 1, 0, 0, true);
}
int main()
{
memset(dp, -1, sizeof dp);
for (; ~scanf("%d", &n);)
printf("%d\n", solve(n));
return 0;
}