数位dp
数位dp大致解决的是一类数字问题:从l到r有多少个数符合某个性质
设立前缀的状态时,一般有前缀的状态为:上一位的值,前缀的数字和,前缀所有数字的gcd,该前缀取模某个数的余数等等
例题:洛谷 P2657
f[i][j]表示数字位数为i,前缀的状态为j的windy数数量
递归版
#include <bits/stdc++.h> #define debug frelimiten("r.txt","r",stdin) #define mp make_pair #define ri register int using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e3+5; const int INF = 0x3f3f3f3f; const int mod = 998244353; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} int f[maxn][maxn],digit[maxn],T; ll n,m; //f数组表示当前将要考虑的是从高到低的第i位(这个位要取0~9任意无限制的数),当前该前缀的状态为st时的数字个数 int dfs(int x,int st,bool limit) //x:第x位(从高位到低位) st:上一位数字(11则表示为前导0) limit:上一位是否达到上限 { if (x==0) return 1; if (!limit && f[x][st]!=-1) return f[x][st]; int up=(limit?digit[x]:9); int ans=0; for (ri i=0;i<=up;i++) { if (abs(st-i)<2) continue; if (st==11 && i==0) ans+=dfs(x-1,11,limit&&(i==up)); else ans+=dfs(x-1,i,limit&&(i==up)); } if (!limit) f[x][st]=ans; return ans; } int solve(int x) { memset(f,-1,sizeof(f)); int len=0; for(;x;x/=10) { digit[++len]=x%10; } return dfs(len,11,true); } int main() { T=1; while (T--) { n=read(),m=read(); cout<<solve(m)-solve(n-1)<<endl; } return 0; }
非递归版
#include <bits/stdc++.h> #define debug frelimiten("r.txt","r",stdin) #define mp make_pair #define ri register int using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 5e3+5; const int INF = 0x3f3f3f3f; const int mod = 998244353; const int N=10005,M=4000005; inline ll read(){ll s=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return s*w;} ll qpow(ll p,ll q){return (q&1?p:1)*(q?qpow(p*p%mod,q/2):1)%mod;} int p,q,dp[15][15],digit[15]; void init() { for (ri i=0;i<=9;i++) dp[1][i]=1; for (ri i=2;i<=10;i++) for (ri j=0;j<=9;j++) for (ri k=0;k<=9;k++) if (abs(j-k)>=2) dp[i][j]+=dp[i-1][k]; } int solve(int x) { memset(digit,0,sizeof(digit)); int len=0,ans=0; while (x) { digit[++len]=x%10; x/=10; } /* 000x 00xx 0xxx... */ //求len-1位的windy数 必定包含在区间里的 for (ri i=1;i<len;i++) for (ri j=1;j<=9;j++) ans+=dp[i][j]; /* 1xxx 2xxx 3xxx . . (a-1)xxxx */ //然后是len位 但最高位<digit[len]的windy数 也包含在区间里 for (ri i=1;i<digit[len];i++) ans+=dp[len][i]; /* a [0,b)[0,c)[0,d)... */ for (ri i=len-1;i;i--) { for (ri j=0;j<digit[i];j++) if (abs(j-digit[i+1])>=2) ans+=dp[i][j]; if (abs(digit[i+1]-digit[i])<2) break; if(i==1) ans+=1; } return ans; } int main() { init(); cin>>p>>q; cout<<solve(q)-solve(p-1)<<endl; return 0; }