P4124 [CQOI2016]手机号码

链接:https://www.luogu.org/problemnew/show/P4124

题目描述

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

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

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

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 复制

12121284000 12121285550

输出样例#1: 复制

5

说明

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

数据范围: 10^10≤L≤R<10^11 。

题解:数位dp, 还是挺容易yy出来的,但要去掉前导0的情况,<10^10是不合法的

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rt register
const int M = 2530;
ll dp[20][10][2][2][2][2];
int digit[20], cnt, tot, bg[M];

ll dfs(int dep, bool f, bool two, bool ok, int t, bool a4, bool a8){
    if(!dep) return (( !(a4 & a8) ) & ok);
    if(!f && dp[dep][t][two][ok][a4][a8] != -1) return dp[dep][t][two][ok][a4][a8];
    rt int i = f ? digit[dep] : 9;
    ll tmp = 0;
    for(; i >= 0; i--){
        if(dep==tot&&i==0) continue;
        bool ntwo, nok = ok;
        if(two && (t == i)) ntwo = 0, nok = 1;
        else if(i == t) ntwo = 1;
        else ntwo = 0;
        tmp += dfs(dep - 1, f & (i == digit[dep]), ntwo, nok, i, a4 | (i == 4), a8 | (i == 8));
    }
    if(!f)dp[dep][t][two][ok][a4][a8] = tmp;
    return tmp;
}

ll get(ll a){
    memset(dp, -1, sizeof(dp));
    tot = 0;
    while(a){
        digit[++tot] = a%10;
        a /= 10;
    }
    if(tot<11) return 0;
    ll ans = dfs(tot, 1, 0, 0, 0, 0, 0);
    return ans;
}
ll read(){
    ll x = 0; ll f = 1; char c = getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c<=‘9‘&&c>=‘0‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}

int main(){
    ///freopen("1.in","r",stdin);
    //freopen("my.out","w",stdout);
    int T;
    memset(dp, -1, sizeof(dp));
    ll L = read(), R = read();
    ll ans2 = get(L - 1);
    ll ans1 = get(R);
    //cout<<ans1<<" "<<ans2;
    cout<<ans1-ans2<<endl;
}
//10000000000 10000012345

原文地址:https://www.cnblogs.com/EdSheeran/p/9483273.html

时间: 2024-10-10 15:12:30

P4124 [CQOI2016]手机号码的相关文章

【luogu4124】【bzoj4521】 [CQOI2016]手机号码 [数位dp]

P4124 [CQOI2016]手机号码 4521 这道题要注意卡上下界 我错了 写dfs版的更好考虑状态 写纯方程转移那个细节把我想瓜了 1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 #include<cmath> 6 #include<stack> 7 #include<algorithm> 8 using

Bzoj4521 [Cqoi2016]手机号码

Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 557  Solved: 335 Description 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不 吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号 码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数 量. 工具需要检测的号码特征有两个:号码中要出现至少3个相邻的相同数字,号码中不能同 时

【luoguP4124 】[CQOI2016]手机号码

题目描述 人们选择手机号码时都希望号码好记.吉利.比如号码中含有几位相邻的相同数字.不含谐音不吉利的数字等.手机运营商在发行新号码时也会考虑这些因素,从号段中选取含有某些特征的号码单独出售.为了便于前期规划,运营商希望开发一个工具来自动统计号段中满足特征的号码数量. 工具需要检测的号码特征有两个:号码中要出现至少 3 个相邻的相同数字:号码中不能同时出现 8 和 4.号码必须同时包含两个特征才满足条件.满足条件的号码例如:13000988721.23333333333.14444101000.而

bzoj 4521: [Cqoi2016]手机号码

感觉get到了一种数位dp的新姿势,加一位表示当前要填的数有没有限制(感觉以前的写法都太蠢了). 这么写有两个地方要注意: 1.每dp到一位时需要f[i][初始状态]++,相当于这位前都是前导零(这道题我把前两位填了两个10作为初始状态). 2.因为有了1,所以初始状态后的第一位不能填0,需要特判 f[i][j][k][l][p][q][o]表示填到第几位,上上位和上位分别是什么,4,8是否出现过,三个连续的是否出现过,以及当前位是否有限制. #include<iostream> #inclu

4521: [Cqoi2016]手机号码|数位DP

数据范围这么小..感觉暴力可过啊.. DP也是随便设计状态 F[i][j][k][s][l] 表示前i位,最后一位是j 最后一位连续出现k次(如果k已经等于3那么就一直不变)s表示4,8的出现状态 l表示前缀是否和原数的前缀相同 转移就是枚举下一位转移,也很简单.. #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio>

【题解】Luogu P4121 [CQOI2016]手机号码 数位DP

题目传送门 Solution 这里我采用记忆化搜索的形式实现有哪个大佬可以教教我递推板的数位dp啊 搜索的过程中记录是否曾出现8,是否曾出现4,是否曾有连续3个相同数字,搜到底返回就可以了 其实没有太多好写的,数位DP很多都是套模板 那还写来干嘛? 这道题 从$1e10$开始算! 从$1e10$开始算! 从$1e10$开始算! 这就意味着我们需要特殊处理最高位,而不是像普通的数位DP那样直接把最高位也丢进搜索里搜. 惨痛的70分教训大概只有我是这么蠢了嘤嘤嘤 Code #include <cst

【题解】Luogu P4121 [CQOI2016]手机号码

Solution 这里我采用记忆化搜索的形式实现有哪个大佬可以教教我递推板的数位dp啊 搜索的过程中记录是否曾出现8,是否曾出现4,是否曾有连续3个相同数字,搜到底返回就可以了 其实没有太多好写的,数位DP很多都是套模板 那还写来干嘛? 这道题 从\(1e10\)开始算! 从\(1e10\)开始算! 从\(1e10\)开始算! 这就意味着我们需要特殊处理最高位,而不是像普通的数位DP那样直接把最高位也丢进搜索里搜. 惨痛的70分教训大概只有我是这么蠢了嘤嘤嘤 Code #include <cst

[CQOI2016]手机号码

题面 是不是所有的数位DP都长得一样? (也许是我太菜,只做过最简单的?) 这题也一样,只不过记得东西变了变,直接记搜即可. 代码真的很短,注释在代码里. #include <bits/stdc++.h> using namespace std; typedef long long ll; int num[12],len; ll l,r,dp[11][11][11][2][2][2][2]; ll f(int p,int a,int b,bool c,bool d,bool _4,bool _

[Solution] [CQOI2016]手机号码

是一道数位DP.可以用记忆化搜索解决. 题目地址 这道题细节非常多.可以考虑分步进行. 求\(L?\)到\(R?\)之间的符合条件的数,可以等价为求\(\text{1~R}?\)之间的符合条件的数减去$text{1~L-1}$之间符合条件的数. 那我先写一个搜索求解\(L?\): void DFS (int X , int K , int U , int _4 , int _8 , int T) { //变量名介绍:X代表当前的位数,K代表当前位所填的数字,U=1表示卡上界,否则不卡._4和_8