BZOJ 4521 CQOI 2016 手机号码 数位DP

4521: [Cqoi2016]手机号码

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 539  Solved: 325
[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][k1][k2][k3][k4][k5]。

表示做到第i位,放的数为j,最后连续两位是否相同,是否存在连续三位相同,是否出现过4,是否出现过8,前缀与原数前缀是否相同。

转移也是比较简单的,枚举上面的东西,再枚举第i+1位填什么,根据题意转移即可。

但是需要注意的是,初始化的时候需要弄一个第10位出来,否则判连续两位相同会比较伤。

而且一般情况下都是cal(r)-cal(l-1),如果固定11位来做的话就会出现问题,这时候可以用开区间来做,变成cal(r+1)-cal(l)。

Code

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <cstring>
 4 #include <string>
 5 #include <algorithm>
 6
 7 using namespace std;
 8
 9 #define FIO "a"
10 #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
11 #define mset(a, b) memset(a, b, sizeof(a))
12 const int maxn = 15;
13 typedef long long LL;
14 LL f[maxn][maxn][2][2][2][2][2], l, r;
15 //×?μ?μúi??£?×?????ê?j£?×?oóá???êyê?·?á?D?£?ê?·?óDá?D?èy???àí?£?ê?·?óD4£?ê?·?óD8£??°×oó??-êy?°×oμ?1??μ
16
17 LL cal(LL x)
18 {
19     mset(f, 0);
20     LL ans = 0; int len = 0, digit[15], a, b, c, d, e;
21     while (x) { digit[++len] = x%10, x /= 10; }
22     reverse(digit+1, digit+len+1);
23     f[0][10][0][0][0][0][1] = 1;
24     REP(i, 0, len-1)
25         REP(j, 0, 10)
26             REP(k1, 0, 1)
27                 REP(k2, 0, 1)
28                     REP(k3, 0, 1)
29                         REP(k4, 0, 1)
30                             REP(k5, 0, 1)
31                                 if (f[i][j][k1][k2][k3][k4][k5])
32                                     REP(k, 0, 9)
33                                     {
34                                         if (k5 && k > digit[i+1]) continue ;
35                                         if (k == j) a = 1; else a = 0;
36                                         if (!k2) b = (a+k1 == 2); else b = k2;
37                                         if (!k3) c = (k == 4); else c = k3;
38                                         if (!k4) d = (k == 8); else d = k4;
39                                         if (c+d == 2) continue ;
40                                         if (k5 && k == digit[i+1]) e = 1; else e = 0;
41                                         f[i+1][k][a][b][c][d][e] += f[i][j][k1][k2][k3][k4][k5];
42                                     }
43     REP(j, 0, 9)
44         REP(k1, 0, 1)
45             REP(k3, 0, 1)
46                 REP(k4, 0, 1)
47                 {
48                     if (k3 && k4) continue ;
49                     ans += f[len][j][k1][1][k3][k4][0];
50                 }
51     return ans;
52 }
53
54 int main()
55 {
56 //    freopen(FIO ".in", "r", stdin);
57 //    freopen(FIO ".out", "w", stdout);
58     scanf("%lld %lld", &l, &r);
59     printf("%lld\n", cal(r+1)-cal(l));
60     return 0;
61 }

时间: 2024-08-14 16:50:50

BZOJ 4521 CQOI 2016 手机号码 数位DP的相关文章

[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-4521】手机号码 数位DP

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

bzoj 1026 [SCOI2009]windy数 数位dp

1026: [SCOI2009]windy数 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1026 Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个windy数? Input 包含两个整数,A B. Output 一个整数.

bzoj 1223: [HNOI2002]Kathy函数 数位DP 高精度

1223: [HNOI2002]Kathy函数 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 207  Solved: 90[Submit][Status] Description Input 仅有一行,为正整数m Output 输出仅有一个正整数,表示所有的满足f(n)=n,(n<=m) 的自然数的个数. Sample Input 5 Sample Output 3 这道题的高精度模板相对又有完善,但还存在bugs,使用时尽量讲位数多开几倍.

BZOJ 2425 [HAOI2010]计数:数位dp + 组合数

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2425 题意: 给你一个数字n,长度不超过50. 你可以将这个数字: (1)去掉若干个0 (2)打乱后重新排列 问你可以产生多少个小于n的数字. 题解: 题目中的第一个操作其实是没有用的. 去掉若干个0之后再重新排列(不允许前导0),和不去0直接重新排列(允许前导0),其实是等价的. 所以按照数位dp的方法从高到低按位统计. 如n = 2345时,分别统计前缀为0~1, 20~22, 23

bzoj 1026 [ SCOI2009 ] windy数 —— 数位DP

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1026 蛮简单的数位DP,预处理 f[i][j] 表示 i 位数,以 j 开头的 windy 数个数: 但不明白为什么最后一位拿出来特判 ret++  不对,而写在循环里,特判 i==1 就对了... 代码如下: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm&g

bzoj 1026 [SCOI2009]windy数——数位dp水题

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1026 迷恋上用dfs写数位dp了. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=15; int l,r,dg[N],dp[N][N]; int dfs(int p,int lst,bo

【题解】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>