bzoj1853: [Scoi2010]幸运数字 dp+容斥原理

在中国,很多人都把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和bOutput输出数据是一行,包括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

显然容斥,剩下的就是黑科技的问题了。 
容斥的话,就是我们首先预处理出来所有的幸运数字,然后筛一遍,筛掉所有是其他幸运数字倍数的数避免重复计算。 
然后就是选1个-选2个的lcm+选3个的lcm-…. 
一个dfs搞定即可。 
但是需要注意,dfs内部的lcm我们不可以求出来,因为会爆long long ,是的我也不知道为什么。 
所以我们需要转成double 型比较是否越界。 
另外,我们筛完之后的那些数字最好按照从大到小的顺序排,如果从小到大的话常数大一点点,但就是这么一点点你就过不去,貌似是从大到小的话更能够对一些非法状态早排除吧

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm> 
 6 #define ll long long 
 7 #define N 10001
 8 using namespace std;
 9 
10 ll l,r;
11 int t,n,m;
12 ll ans;
13 ll a[N],b[N];
14 bool vis[N];
15 
16 ll gcd(ll a,ll b)
17 {
18     return b?gcd(b,a%b):a;
19 }
20 void pre(int x,ll y)
21 {
22     if(y>r)return;
23     if(x>0)a[++m]=y;
24     pre(x+1,y*10+6);
25     pre(x+1,y*10+8);
26 }
27 void dfs(int x,int y,ll z)
28 {
29     if(x>n)
30     {
31         if(y&1)ans+=r/z-(l-1)/z;
32         else if(y)ans-=r/z-(l-1)/z;
33         return;
34     }
35     dfs(x+1,y,z);
36     ll tmp=z/gcd(a[x],z);
37     if(((double)a[x]*tmp)<=r) dfs(x+1,y+1,a[x]*tmp);
38 }
39 int main()
40 {
41     scanf("%lld%lld",&l,&r);
42     pre(0,0);
43     sort(a+1,a+m+1);
44     for(int i=1;i<=m;i++)
45        if(!vis[i])
46        {
47               b[++n]=a[i];
48               for(int j=i+1;j<=m;j++)
49                   if(!(a[j]%a[i]))
50                       vis[j]=1;
51        }
52     for(int i=1;i<=n;i++)
53        a[n-i+1]=b[i];
54     dfs(1,0,1);
55     printf("%lld",ans);
56 }

 

posted @ 2017-10-06 20:39  Kaiser-  阅读(307)  评论(0编辑  收藏  举报