【BZOJ-4521】手机号码 数位DP

4521: [Cqoi2016]手机号码

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit:
303  Solved: 194
[Submit][Status][Discuss]

Description

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

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

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

Input

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

10^10 < =  L < =  R < 10^11

Output

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

Sample Input

12121284000 12121285550

Sample Output

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

HINT

Source

Solution

这种数据范围,一眼数位DP,但是不是特别的好搞...

F[i][j][0/1][0/1][0/1][0/1][0/1]表示位数为i,最高位为j,最高位连续两个是否是相同的,是否有连续3个相同的,是否有4,是否有8,前缀和原数前缀的大小关系

枚举这些东西....k1,k2,k3,k4,k5分别表示对应上述0/1

注意&启发:

1.数位DP一般采取预处理,后求和,这里数据范围直接处理非常方便,采用直接处理

2.注意范围$L<=10^{10}$默认10位做的话,不能直接计算Calc(R)-Calc(L-1),这里可以采取讨论,或者处理区间为开区间,计算Calc(R+1)-Calc(L)即可

3.注意状态的枚举,一些零碎的判断需要理清,否则会陷入泥潭(WA了无数,不知所措)

4.如果digit[]正序做更方便可以考虑正序处理,外加此题可以采取特殊的枚举方式

5.更深层次的了解了数位DP,但仍需更多的练习,总体来说,此题是个不错的题目,自己能想到的只是框架,实现仍有些差池

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long F[20][15][2][2][2][2][2],l,r;
int p[20],num;
long long cal(long long x)
{
    memset(F,0,sizeof(F));
    long long ans=0; int len=0,digit[12],a,b,c,d,e;
    while(x){digit[++len]=x%10; x/=10;}
    reverse(digit+1,digit+len+1);
    F[0][10][0][0][0][0][1]=1;
    for (int i=0; i<=len-1; i++)
        for (int j=0; j<=10; j++)
            for (int k1=0; k1<=1; k1++)
                for (int k2=0; k2<=1; k2++)
                    for (int k3=0; k3<=1; k3++)
                        for (int k4=0; k4<=1; k4++)
                            for (int k5=0; k5<=1; k5++)
                                if (F[i][j][k1][k2][k3][k4])
                                    for (int k=0; k<=9; k++)
                                        {
                                            if (k5 && (k>digit[i+1])) continue;
                                            if (k==j) a=1; else a=0;
                                            if (k2==0) b=(k1+a)==2; else b=k2;
                                            if (k3==0) c=(k==4); else c=k3;
                                            if (k4==0) d=(k==8); else d=k4;
                                            if ((c+d)==2) continue;
                                            if (k5 && (k==digit[i+1])) e=1; else e=0;
                                            F[i+1][k][a][b][c][d][e]+=F[i][j][k1][k2][k3][k4][k5];
                                        }
    for (int i=0; i<=9; i++)
        for (int k1=0; k1<=1; k1++)
            for (int k3=0; k3<=1; k3++)
                for (int k4=0; (k4<=1)&&(k4+k3<2); k4++)
                    ans+=F[len][i][k1][1][k3][k4][0];
    return ans;
}
int main()
{
    scanf("%lld%lld",&l,&r);
    printf("%lld\n",cal(r+1)-cal(l));
    return 0;
}

昨晚写了道数(S)位(W)DP,结果第二天期中数学(S)物理(W)血崩...虽然1个多月的课,就上了1/4不到...但心里不爽啊o_O

时间: 2024-08-27 14:11:10

【BZOJ-4521】手机号码 数位DP的相关文章

BZOJ 4521 CQOI 2016 手机号码 数位DP

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

BZOJ 4521 手机号码

SB数位dp. 我的貌似要特判9999999999的情况. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; long long l,r,dp[13][4][10][2][2][2],ret=0,bit[15]; void reset() { for (long long i=0;i<=11;i++) for (l

BZOJ 3329 Xorequ 数位DP+矩阵乘法

题目大意:给定n,求[1,n]以内以及[1,2^n]以内有多少x满足x^3x=2x x^3x=2x等价于x^2x = 3x 而3x=x+2x 且2x=x<<1 故x满足条件当且仅当x&(x<<1)=0 故x的二进制拆分中任意两个1不相邻 令f[i]为i位数中最高位为0的满足条件的数的数量 g[i]为i位数中最高位为1的满足条件的数的数量 则显然有 f[i+1]=f[i]+g[i] g[i+1]=f[i] 于是第一问数位DP 第二问矩阵乘法即可 #include <cs

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

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

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>

【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

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圆环数(数位DP+恶心细节)

BZOJ 1662: [Usaco2006 Nov]Round Numbers 圆环数 Time Limit: 5 Sec  Memory Limit: 64 MB Description 正如你所知,奶牛们没有手指以至于不能玩“石头剪刀布”来任意地决定例如谁先挤奶的顺序.她们甚至也不能通过仍硬币的方式. 所以她们通过"round number"竞赛的方式.第一头牛选取一个整数,小于20亿.第二头牛也这样选取一个整数.如果这两个数都是 "round numbers"

[bzoj 1026]windy数(数位DP)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1026 分析: 简单的数位DP啦 f[i][j]表示数字有i位,最高位的数值为j的windy数总个数 那么f[i][j]=singma(f[i-1][k])(|j-k|>=2) 那么对于1~x(假设x从高到低的每位依次是x[n],x[n-1],……x[1])中的windy数个数就是f[n][0]+f[n][1]+……f[n][x[n]-1] + f[n-1][0]+f[n-1][1]+……f[

[BZOJ 1833] [ZJOI2010] count 数字计数 【数位DP】

题目链接:BZOJ - 1833 题目分析 数位DP .. 用 f[i][j][k] 表示第 i 位是 j 的 i 位数共有多少个数码 k . 然后差分询问...Get()中注意一下,如果固定了第 i 位,这一位是 t ,那么数码 t 的答案是要加一个值的(见代码). 代码 #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <cmat