1853: [Scoi2010]幸运数字
Time Limit: 2 Sec Memory Limit: 64 MBSubmit: 2117 Solved: 779
[Submit][Status][Discuss]
Description
在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。 现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。
Input
输入数据是一行,包括2个数字a和b
Output
输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数
Sample Input
【样例输入1】
1 10
【样例输入2】
1234 4321
1 10
【样例输入2】
1234 4321
Sample Output
【样例输出1】
2
【样例输出2】
809
2
【样例输出2】
809
HINT
【数据范围】
对于30%的数据,保证1 < =a < =b < =1000000
对于100%的数据,保证1 < =a < =b < =10000000000
Source
分析
不难想到用容斥原理来统计。
先预处理出所有小于1e10的幸运数字,并不是很多。但是发现枚举所有的组合还是会爆炸的,需要一些剪枝。
1. 对于两个幸运数字,x<y,如果有y为x的倍数,则y可以忽略,因为x可以完全覆盖y的倍数。
2. 对于一种组合,如果目前的积已经大于N,即再进行下去得到的都是加减0的无意义操作,可以直接跳出。
3. 可以把GCD函数写成非递归的形式,但貌似没多大用,跑出来的结果差距不是很大,也许是我写得不好。
4. 枚举的时候从大往小枚举,据说有奇效,因为懒癌晚期,我并没有对比验证。
代码
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 typedef long long LL; 6 7 const int N = 200020; 8 9 const LL lim = 10000000000LL; 10 11 template <class T> 12 __inline void read(T &x) 13 { 14 x = 0; char c = getchar(); 15 16 while (c < '0') 17 c = getchar(); 18 19 while (c >= '0') 20 { 21 x = x*10 + c - '0'; 22 c = getchar(); 23 } 24 } 25 26 LL gcd(LL a, LL b) 27 { 28 if (a < b) 29 { 30 a ^= b; 31 b ^= a; 32 a ^= b; 33 } 34 while (b) 35 { 36 a %= b; 37 a ^= b; 38 b ^= a; 39 a ^= b; 40 } 41 return a; 42 } 43 44 LL num[N]; int tot = 0; 45 46 __inline void prework(void) 47 { 48 int t, tail = 0; 49 50 for (t = 0; num[t] <= lim; ++t) 51 { 52 num[++tail] = num[t] * 10 + 6; 53 num[++tail] = num[t] * 10 + 8; 54 } 55 56 for (int i = 1; i <= t; ++i) 57 { 58 bool flag = true; 59 for (int j = 1; j <= tot; ++j) 60 if (num[i] % num[j] == 0) 61 { flag = false; break; } 62 if (flag)num[++tot] = num[i]; 63 } 64 } 65 66 LL answer, limit; 67 68 void search(int t, bool f, LL sum) 69 { 70 if (t) 71 { 72 search(t - 1, f, sum); 73 74 LL GCD = gcd(num[t], sum); 75 76 if (sum / GCD <= limit / num[t]) 77 { 78 LL LCM = sum / GCD * num[t]; 79 80 if (f) 81 answer -= limit / LCM; 82 else 83 answer += limit / LCM; 84 85 search(t - 1, !f, LCM); 86 } 87 } 88 } 89 90 __inline LL count(LL n) 91 { 92 limit = n; 93 answer = 0; 94 95 int pos = 1; 96 97 while (pos <= tot 98 && num[pos] <= n)++pos; 99 100 search(pos - 1, 0, 1); 101 102 return answer; 103 } 104 105 signed main(void) 106 { 107 prework(); 108 109 LL a; read(a); 110 LL b; read(b); 111 112 printf("%lld ", 113 count(b) 114 - count(a - 1) 115 ); 116 }
@Author: YouSiki