题目
多组样例,每次给定一个n(1<=n<=1e9),
求[1,n]中,数位表示中包含"13"子串,且数字能被13整除的数的数量
思路来源
https://blog.csdn.net/Code92007/article/details/86825530
题解
简单题,我思路来源我自己
其实就是 Bomb 那道题的思想,感觉一年多,终于把其理解透彻了,
也会写记忆化搜索的了,万一哪天忘了呢,总结一下吧,
dp[i][j][k]表示当前第i位 状态为j 当前%13余数为k的方案数
状态有012三种,分别对应不含13且末尾不是1,不含13且末尾是1,已含13三种状态,
再根据最后填什么,判断和13前缀匹配多少,
%13=0,加一维余数,用于记忆化
代码
#include<bits/stdc++.h>
using namespace std;
const int N=20,mod=13;
typedef long long ll;
int t,k,len;
//不含13且末尾不是1 不含13且末尾是1 已含13(分别对应状态0 1 2)
ll l,r,a[N],sz[N][3][mod];//dp[i][j][k]:表示i位 当前状态为j 当前余数为k的方案数
ll dfs(int pos,int st,int sum,bool lim){
//printf("st:%d sum:%d\n",st,sum);
if(pos==0){
return st==2 && sum==0;
}
if(~sz[pos][st][sum] && !lim)return sz[pos][st][sum];
ll ret=0;
int up=lim?a[pos]:9;
if(st==2){
for(int i=0;i<=up;++i){
ret+=dfs(pos-1,st,(sum*10+i)%mod,lim && i==up);
}
}
else if(st==1){
for(int i=0;i<=up;++i){
ret+=dfs(pos-1,(i==3?2:(i==1?1:0)),(sum*10+i)%mod,lim && i==up);
}
}
else if(st==0){
for(int i=0;i<=up;++i){
ret+=dfs(pos-1,i==1?1:0,(sum*10+i)%mod,lim && i==up);
}
}
if(!lim)sz[pos][st][sum]=ret;
return ret;
}
ll part(ll x){
len=0;
while(x)a[++len]=x%10,x/=10;
return dfs(len,0,0,1);
}
int main(){
memset(sz,-1,sizeof sz);
while(~scanf("%lld",&r)){
printf("%lld\n",part(r));
}
return 0;
}