BZOJ 1853: [Scoi2010]幸运数字(容斥原理)

http://www.lydsy.com/JudgeOnline/problem.php?id=1853

题意:

在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。 现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。

 

思路:

首先要明白一点的是,在N范围内找a或b的倍数的个数为:

那么推广到这题的话也是差不多的,也就是容斥原理的运用。

首先可以先找出所有带6、8的数,可以优化一下删去有倍数关系的数,然后就是从中找lcm,也就是容斥原理的奇加偶减。在寻找的过程中,优先找值大的,这样一来剪枝会剪得更多一些。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<sstream>
 6 #include<vector>
 7 #include<stack>
 8 #include<queue>
 9 #include<cmath>
10 #include<map>
11 #include<set>
12 using namespace std;
13 typedef long long ll;
14 typedef pair<int,int> pll;
15 const int INF = 0x3f3f3f3f;
16 const int maxn = 1e5 + 5;
17 
18 int n;
19 ll l, r;
20 ll cnt;
21 ll ans;
22 ll a[maxn];
23 ll b[maxn];
24 int vis[maxn];
25 
26 void init(ll x)
27 {
28     if(x>r)  return;
29     if(x!=0)  a[cnt++]=x;
30     init(x*10+6);
31     init(x*10+8);
32 }
33 
34 ll gcd(ll a, ll b)
35 {
36     return b==0?a:gcd(b,a%b);
37 }
38 
39 void dfs(int cur, int cnt, ll val)
40 {
41     if(cur>=n)
42     {
43         if(!cnt)  return;
44         if(cnt&1)   ans+=(r/val-(l-1)/val);
45         else ans-=(r/val-(l-1)/val);
46         return;
47     }
48     dfs(cur+1,cnt,val);
49     ll tmp=val/(gcd(a[cur],val));  
50     if((double)tmp*a[cur]<=r)
51     {
52         dfs(cur+1,cnt+1,tmp*a[cur]);
53     }
54 }
55 
56 int main()
57 {
58     //freopen("in.txt","r",stdin);
59     while(~scanf("%lld%lld",&l,&r))
60     {
61         cnt=0;
62         init(0);
63 
64         n=0;
65         sort(a,a+cnt);
66         memset(vis,0,sizeof(vis));
67         for(int i=0;i<cnt;i++)
68         {
69             if(!vis[i])
70             {
71                 b[n++]=a[i];
72                 for(int j=i+1;j<cnt;j++)
73                 {
74                     if(a[j]%a[i]==0)
75                         vis[j]=1;
76                 }
77             }
78         }
79         for(int i=0;i<n;i++)
80             a[i]=b[n-i-1];
81 
82         ans=0;
83         dfs(0,0,1);
84         printf("%lld\n",ans);
85     }
86     return 0;
87 }
posted @ 2017-07-28 17:22  Kayden_Cheung  阅读(207)  评论(0编辑  收藏  举报
//目录