洛谷 P4124 [CQOI2016]手机号码

题目描述

题意

人们选择手机号码时都希望号码好记、吉利。比如号码中含有几位相邻的相同数字、不含谐音不吉利的数字等。手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售。为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量。

工具需要检测的号码特征有两个:号码中要出现至少 3 个相邻的相同数字;号码中不能同时出现 8 和 4。号码必须同时包含两个特征才满足条件。满足条件的号码例如:13000988721、23333333333、14444101000。而不满足条件的号码例如:1015400080、10010012022。

手机号码一定是 11 位数,前不含前导的 0。工具接收两个数 L* 和 R*,自动统计出 [L,R]区间内所有满足条件的号码数量。L 和 R也是 11位的手机号码。

输入格式

输入文件内容只有一行,为空格分隔的 2 个正整数 L,R。

输出格式

输出文件内容只有一行,为 1 个整数,表示满足条件的手机号数量。

输入输出样例

输入

12121284000 12121285550

输出

5

说明/提示

样例解释:满足条件的号码: 12121285000、 12121285111、 12121285222、 12121285333、 12121285550。

数据范围:1010\(\leq\)L\(\leq\)R<1011

分析

一开始我以为这又是一道板子题,只不过状态有点多,大概要开到6维

于是我就写了下面的代码交了上去

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
ll f[13][13][13][3][3][3],num[30];
ll a,b;
ll aaaa=0;
ll asd(ll now,ll bef,ll beff,bool jud4,bool jud8,bool jud3,bool lim){
    if(now<0){
        if(jud3 && (!jud4 || !jud8)) return 1;
        return 0;
    }
    if(f[now][bef][beff][jud4][jud8][jud3]!=-1 && !lim){
        return f[now][bef][beff][jud4][jud8][jud3];
    }
    ll mmax=9;
    if(lim==1) mmax=num[now];
    ll anss=0;
    for(ll i=0;i<=mmax;i++){
        bool pd=0;
        if(i==bef && bef==beff) pd=1;
        anss+=asd(now-1,i,bef,max(jud4,i==4),max(jud8,i==8),max(jud3,pd),lim&(i==mmax));
    }
    if(!lim) f[now][bef][beff][jud4][jud8][jud3]=anss;
    return anss;
}
ll solve(ll xx){
    ll yy=xx;
    ll cnt=0;
    memset(num,0,sizeof(num));
    while(xx){
        ll aa=xx%10;
        num[cnt++]=aa;
        xx/=10;
    }
    return asd(cnt-1,0,0,0,0,0,1);
}
int main(){
    memset(f,-1,sizeof(f));
    scanf("%lld%lld",&a,&b);
    ll ans=solve(b)-solve(a-1);
    printf("%lld\n",ans);
    return 0;
}


样例过了,还不错,于是我就信心满满地交了上去,结果发现WA,70分

一开始我认为是前缀0的问题,但是一想,其实并没有那么简单,为什么呢

因为题目中给出的数据范围是1010\(\leq\)L\(\leq\)R<1011

也就是说,第一位并不会出现0的情况(我一开始是这么想的

即使出现了0,那么你是把00012345679判成是电话号码还是不是呢,毕竟电话号码和数字不一样

于是我就查看了一下WA的数据,发现了一个神奇的问题

​ WA数据:

输入:
10000000000 10002098555
答案
1685496
用户输出
4821665153

输入:
10000000000 23456271996
答案:
796184381
用户输出
5616164038

输入:
10000000000 53628881996
答案:
2186241614
用户输出
7006221271

WA的数据竟然都是以10000000000开始的,也就是符合要求的最小的数

这会带来什么问题呢?

聪明的你应该已经想到了

我们差分是所传的参数是solve(bb)-solve(aa-1)

也就是说,如果给出的数据是10000000000的话,我们传到solve函数中会变成了9999999999,这出现什么问题呢

这会导致前缀0的增多

因为我的函数中传了当前这一位前面的第一位以及前面的第二位

如果我们是按1010算的话12332112会被算成电话号码,而如果按9*109算的话上面的数就不会被算出电话号码

所以我们只要特判,把1010的结果提前处理就可以了

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;
typedef long long ll;
ll f[13][13][13][3][3][3],num[30];
ll a,b;
ll aaaa=0;
ll asd(ll now,ll bef,ll beff,bool jud4,bool jud8,bool jud3,bool lim){
    if(now<0){
        if(jud3 && (!jud4 || !jud8)) return 1;
        return 0;
    }
    if(f[now][bef][beff][jud4][jud8][jud3]!=-1 && !lim){
        return f[now][bef][beff][jud4][jud8][jud3];
    }
    ll mmax=9;
    if(lim==1) mmax=num[now];
    ll anss=0;
    for(ll i=0;i<=mmax;i++){
        bool pd=0;
        if(i==bef && bef==beff) pd=1;
        anss+=asd(now-1,i,bef,max(jud4,i==4),max(jud8,i==8),max(jud3,pd),lim&(i==mmax));
    }
    if(!lim) f[now][bef][beff][jud4][jud8][jud3]=anss;
    return anss;
}
ll solve(ll xx){
    ll yy=xx;
    ll cnt=0;
    memset(num,0,sizeof(num));
    while(xx){
        ll aa=xx%10;
        num[cnt++]=aa;
        xx/=10;
    }
    if(yy==9999999999) return 5899826978;
    return asd(cnt-1,19,12,0,0,0,1);
}
int main(){
    memset(f,-1,sizeof(f));
    scanf("%lld%lld",&a,&b);
    ll ans=solve(b)-solve(a-1);
    printf("%lld\n",ans);
    return 0;
}


据说有很多题解过不了10000000000 10000000000这组数据

(应该输出1)

posted @ 2020-04-22 16:51  liuchanglc  阅读(209)  评论(0编辑  收藏  举报