P4124 [CQOI2016]手机号码(数位DP)
题目描述
人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。
工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字;号码中不能同时出现 88 和 44。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 1111 位数,前不含前导的 00。工具接收两个数 LL 和 RR,自动统计出 [L,R][L,R] 区间内所有满足条件的号码数量。LL 和 RR 也是 1111 位的手机号码。
输入格式
输入文件内容只有一行,为空格分隔的 22 个正整数 L,RL,R。
输出格式
输出文件内容只有一行,为 11 个整数,表示满足条件的手机号数量。
#include<bits/stdc++.h>
using namespace std;
const int maxn=70;
const int mod=1e9+7;
typedef long long ll;
ll L,R,f[maxn][15][15][2][2][2];
//f(i,j,k,l)表示当前到第i位,上一位数字,上上位数字,是否有8和4,是否有3位连续相同的数字的状态
int a[maxn],len;
int tot=0;
ll dfs (int pos,int pre1,int pre2,int st,int limit,bool x,bool y,bool z) {
if (pos<=0) {
if (z==1&&!(x&&y)) return 1;
return 0;
}
if (!limit&&f[pos][pre1][pre2][x][y][z]!=-1) {
return f[pos][pre1][pre2][x][y][z];
}
ll ans=0;
ll k=limit?a[pos]:9;
for (int i=0;i<=k;i++) {
if (st&&i==0) {
ans+=dfs(pos-1,pre1,pre2,1,limit&&i==k,x,y,z);
}
else {
if (i==pre1&&i==pre2)
ans+=dfs(pos-1,i,pre1,0,limit&&i==k,(x||(i==8)),(y||(i==4)),1);
else
ans+=dfs(pos-1,i,pre1,0,limit&&i==k,(x||(i==8)),(y||(i==4)),z);
}
}
if (!limit&&!st) {
f[pos][pre1][pre2][x][y][z]=ans;
}
return ans;
}
ll solve (ll x){
len=0;
while (x) a[++len]=x%10,x/=10;
memset(f,-1,sizeof(f));
return dfs(len,10,10,1,1,0,0,0);
}
int main () {
cin>>L>>R;
cout<<solve(R)-solve(L-1);
}