Scoi 2010 幸运数字

【题目描述】
在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。
现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。
【输入】
输入数据是一行,包括2个数字a和b
【输出】
输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数
【样例输入1】
1 10
【样例输出1】
2
【样例输入2】
1234 4321
【样例输出2】
809
【数据范围】
对于30%的数据,保证1<=a<=b<=1000000
对于100%的数据,保证1<=a<=b<=10000000000

思路:
先找出幸运号码 然后把存在倍数关系的去除
然后用容斥原理 加单个数 减2个数lcm 加3个数lcm....
注意:以前用的时候都是质数 所以当时只要乘积(我开始就是用乘积 坑爹)
从大到小容斥 不然会TEL(我又TEL好久了)
从大到小会少好多无用计算,毕竟从小到大越后面越难有符合的
#include <iostream> #include <string> #include<sstream> #include <cmath> #include <map> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define LL long long LL dp[2400]; int h[2400]; int num; LL ans; void dfs(LL n,int deep) { if(deep>10) return; dp[num++]=n*10+6; dfs(n*10+6,deep+1); dp[num++]=n*10+8; dfs(n*10+8,deep+1); } void init() { num=0; dfs(0,1); sort(dp,dp+num); int i,j; for(i=0;i<num;i++) if(!h[i]) for(j=i+1;j<num;j++) if(dp[j]%dp[i]==0) h[j]=1; j=0; for(i=0;i<num;i++) if(!h[i]) dp[j++]=dp[i]; num=j; for(i=0;i<num/2;i++) swap(dp[i],dp[num-i-1]); } LL gcd(LL a,LL b) { LL r; while(r=a%b){a=b;b=r;} return b; } LL a,b; void dfss(int deep,int index,LL val) { LL t; for(int i=index;i<num;i++) { if(dp[i]>b) continue; LL g=gcd(val,dp[i]); if((val/g)>(b/dp[i])) continue; t=val/g*dp[i]; if(deep&1) ans+=(b/t-a/t); else ans-=(b/t-a/t); dfss(deep+1,i+1,t); } } int main() { init(); while(scanf("%lld %lld",&a,&b)!=EOF) { ans=0;a--; dfss(1,0,1); printf("%lld\n",ans); } return 0; }

 

posted on 2014-03-25 17:00  江财小子  阅读(429)  评论(0编辑  收藏  举报