P4124 [CQOI2016]手机号码
题目描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少 3 个相邻的相同数字;号码中不能同时出现 888 和 444。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 11 位数
,前不含前导的 000。工具接收两个数 LLL 和 RRR,自动统计出[L,R] 区间内所有满足条件的号码数量。L 和 R 也是 11 位的手机号码。
输入输出格式
输入格式:
输入文件内容只有一行,为空格分隔的 2 个正整数 L,R。
输出格式:
输出文件内容只有一行,为 1 个整数,表示满足条件的手机号数量。
输入样例#1
12121284000 12121285550
说明
样例解释:满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550。
数据范围:1010 ≤ L ≤ R < 1011
注意隐藏条件, 十分坑人!
其余没什么了,数位dp板子一套就OK
Code
#include<cstdio>
#include<cstring>
#include<vector>
#define pb push_back
typedef long long ll;
std::vector <int> Bt;
ll dp[20][20][4][20][4][4];
ll DFS(int k, int flag, int pre, int cnt, int sign, int sign_8, int sign_4){
if(k == -1) return sign;
if(!flag && ~dp[k][pre][sign][cnt][sign_8][sign_4]) return dp[k][pre][sign][cnt][sign_8][sign_4];
int end = flag?Bt[k]:9;
ll tmp = 0;
for(int i = 0; i <= end; i ++){
if(!i && k == 10) continue ;
if(sign_8 && i == 4) continue ;
if(sign_4 && i == 8) continue ;
if(pre == i) tmp += DFS(k-1, i==end&&flag, i, cnt+1, (cnt+1>=3)||sign, i==8||sign_8, i==4||sign_4);
else tmp += DFS(k-1, i==end&&flag, i, 1, sign, i==8||sign_8, i==4||sign_4);
}
if(!flag) dp[k][pre][sign][cnt][sign_8][sign_4] = tmp;
return tmp;
}
ll Work(ll x){
if(x < 10000000000) return 0;
memset(dp, -1, sizeof dp);
Bt.clear();
while(x) Bt.pb(x%10), x /= 10;
return DFS(Bt.size()-1, 1, 0, 0, 0, 0, 0);
}
int main(){
ll L, R;
scanf("%lld%lld", &L, &R);
printf("%lld\n", Work(R)-Work(L-1));
return 0;
}