#2755「CQOI2016」手机号码
题面
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少 3个相邻的相同数字;号码中不能同时出现 8 和 4。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 11 位数,前不含前导的 0。工具接收两个数 L 和 R,自动统计出[L,R] 区间内所有满足条件的号码数量。L 和 R 也是 11 位的手机号码。
输入
输入文件内容只有一行,为空格分隔的两个正整数 L,R
输出
输出文件内容只有一行,为一个整数,表示满足条件的手机号数量。
样例输入
12121284000 12121285550
样例输出
5
提示
样例解释
满足条件的号码有:
12121285000
、
12121285111
、
12121285222
、
12121285333
、
12121285550
。
12121285000、 12121285111、 12121285222、 12121285333、 12121285550。
12121285000、12121285111、12121285222、12121285333、12121285550。
对于 30% 的数据,
R
−
L
≤
1
0
7
R−L≤10^7
R−L≤107
对于 100% 的数据,
1
0
10
≤
L
≤
R
<
1
0
11
10^{10}≤L≤R<10^{11}
1010≤L≤R<1011
SOL
很明显这是一道数位DP题
不过这道题的状态定义真的很绕,我定义了六维,分别表示位数,上个数,上上个数,有没有连续三个数,有没有4,有没有8,然后再做记忆化搜索。
值得注意的是,这道题只能出现11位的数,所以考虑前导0,但是只要你第一位从1开始
D
P
DP
DP就可以了,不过要注意1e10作为左端点的情况,非常坑!!!WA了三个点!!!
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#define int long long
using namespace std;
inline int rd(){
int register data=0;static char ch=0;
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
return data;
}
int f[15][10][10][2][2][2],h[15],a,b;
inline int dp(int pos,int pre,int pre_pre,int tri,int _4,int _8,bool flag){//tri:有连续三个相同的数 _8:有8 _4:有4
if(_4&&_8)return 0;
if(!pos)return tri;
if(!flag&&f[pos][pre][pre_pre][tri][_4][_8]!=-1)return f[pos][pre][pre_pre][tri][_4][_8];
int register maxl=flag?h[pos]:9,ans=0;
for(int register i=0;i<=maxl;i++){
bool register now_flag=(flag&&i==maxl);
int register now_tri=(tri||(pre_pre==pre&&pre==i)),now__4=(_4||i==4),now__8=(_8||i==8);
ans+=dp(pos-1,i,pre,now_tri,now__4,now__8,now_flag);
}
if(!flag)f[pos][pre][pre_pre][tri][_4][_8]=ans;
return ans;
}
inline int solve(int x){
int register len=0,ans=0;
while(x){
h[++len]=x%10;
x/=10;
}
for(int register i=1;i<=h[len];i++){
memset(f,-1,sizeof(f));
ans+=dp(len-1,i,0,0,i==4,i==8,i==h[len]);
}
return ans;
}
signed main(){
a=rd();b=rd();
if(a!=(int)1e10)printf("%lld",solve(b)-solve(a-1));
else printf("%lld",solve(b));
return 0;
}