BZOJ 1853: [Scoi2010]幸运数字

1853: [Scoi2010]幸运数字

Time Limit: 2 Sec  Memory Limit: 64 MB
Submit: 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

Sample Output

【样例输出1】
2
【样例输出2】
809

HINT

【数据范围】
对于30%的数据,保证1 < =a < =b < =1000000
对于100%的数据,保证1 < =a < =b < =10000000000

Source

[Submit][Status][Discuss]

 

分析

不难想到用容斥原理来统计。

先预处理出所有小于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\n", 
113         count(b)
114     -    count(a - 1)
115     );
116 }
BZOJ_1853.cpp

 

@Author: YouSiki

posted @ 2016-11-17 14:19  YouSiki  阅读(317)  评论(0编辑  收藏  举报