题目描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字;号码中不能同时出现 88 和 44。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 1111 位数,前不含前导的 00。工具接收两个数 LL 和 RR,自动统计出 [L,R][L,R] 区间内所有满足条件的号码数量。LL 和 RR 也是 1111 位的手机号码。
输入格式
输入文件内容只有一行,为空格分隔的 22 个正整数 L,RL,R。
输出格式
输出文件内容只有一行,为 11 个整数,表示满足条件的手机号数量。
输入输出样例
输入 #1复制
12121284000 12121285550
输出 #1复制
5
说明/提示
样例解释:满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550。
数据范围:10^{10}\leq L\leq R<10^{11}10
10
≤L≤R<10
11。
比较明显的数位dp,不过状态很多。
我们记录上一个数字,上一个数字的上一个数字,当前是否已经满足条件,是否出现4,是否出现8,是否limit,当前是第几位即可。
前导零可以通过枚举第一位来解决,比较方便。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dp[20][2][10][10][2][2][2],a[20],l,r,flag;
int dfs(int pos,int pre,int st,int ok,int limit,int is4,int is8){
if(is4&&is8) return 0;
if(!pos) return ok;
if(dp[pos][ok][pre][st][limit][is4][is8]!=-1)
return dp[pos][ok][pre][st][limit][is4][is8];
int up=limit?a[pos]:9,res=0;
for(int i=0;i<=up;i++){
res+=dfs(pos-1,i,pre,ok||i==pre&&pre==st,limit&&i==up,i==4||is4,i==8||is8);
}
dp[pos][ok][pre][st][limit][is4][is8]=res;
return res;
}
int solve(int x){
if(x<1e10) return 0; int res=0;
int pos=0; memset(dp,-1,sizeof dp);
while(x) a[++pos]=x%10,x/=10;
for(int i=1;i<=a[pos];i++) res+=dfs(pos-1,i,0,0,i==a[pos],i==4,i==8);
return res;
}
signed main(){
cin>>l>>r;
cout<<solve(r)-solve(l-1)<<endl;
return 0;
}