P4124 [CQOI2016] 手机号码

//URL:
/*
工具需要检测的号码特征有两个:号码中要出现至少 33 个相邻的相同数字;号码中不能同时出现 88 和 44。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。
手机号码一定是 1111 位数,前不含前导的 00。工具接收两个数 LL 和 RR,自动统计出 [L,R][L,R] 区间内所有满足条件的号码数量。LL 和 RR 也是 1111 位的手机号码

// pos    当前位置
// a    上一位数字
// b    再上一位数字
// state 是否出现3个连续相同数字
// eight 是否出现8
// four  是否出现4
f[pos][a][b][state][eight][four]

1 如何避开前导零 从头开始枚举
*/
/*
12121284000 12121285550
5

*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<string.h>
#include<queue>
#include<vector>
#include<bits/stdc++.h>
typedef long long ll;
#define ddd printf("-----------------------\n");
using namespace std;
const int maxn=1e1 +10;
const int mod=998244353;
const int inf=0x3f3f3f3f;

int num[15];
ll L,R,f[13][10][10][2][2][2];

ll dfs(int pos,int a,int b,int sta,int et,int fr,int lim)
{
    if(et&&fr) return 0;
    if(pos==0) return sta;
    if(lim==0&&f[pos][a][b][sta][et][fr]!=-1) return f[pos][a][b][sta][et][fr];
    int end=lim? num[pos]:9;
    ll res=0;
    for(int i=0;i<=end;i++){
        res+=dfs(pos-1,i,a,sta||(i==a&&i==b),et||i==8,fr||i==4,lim&&i==end);
    }
    if(lim==0) f[pos][a][b][sta][et][fr]=res;
    return res;
}

ll solve(ll x)
{
    int len=0;
    memset(f,-1,sizeof(f));
    while(x)
    {
        num[++len]=x%10;
        x/=10;
    }
    if(len!=11) return 0;
    ll ans=0;
    for(int i=1;i<=num[len];i++)
        ans+=dfs(len-1,i,0,0,i==8,i==4,i==num[len]);
    //ans=dfs(len,0,0,0,0,0,1);
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>L>>R;
    cout<<solve(R)-solve(L-1)<<'\n';
   
    return 0;
}

 

posted @ 2024-02-08 17:34  JMXZ  阅读(8)  评论(0)    收藏  举报