题目背景
四川NOI省选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
说明
对于30%的数据,保证1<=a<=b<=1000000
对于100%的数据,保证1<=a<=b<=10000000000
思路:打表+容斥原理+剪枝
先打一张幸运数字的表;
然后容斥原理求值;
剪枝:有些(是前面的幸运数字的倍数)幸运数字是无用的。
从大到小排。
超出边界的最小公倍数是无用的。
代码实现:
1 #include<cstdio> 2 #include<algorithm> 3 #define LL long long 4 using namespace std; 5 LL l,r,ans; 6 LL a,b; 7 LL s[3000],ss; 8 LL fs[3000],fss; 9 LL lch,ret; 10 char ch[30]; 11 LL read(){ 12 while(ch[0]=getchar(),ch[0]<'0'||ch[0]>'9'); 13 lch=1,ret=ch[0]-'0'; 14 while(ch[lch]=getchar(),ch[lch]>='0'&&ch[lch]<='9') ret=ret*10+ch[lch]-'0',lch++; 15 return ret; 16 } 17 void write(LL x){ 18 if(!x) return; 19 write(x/10); 20 putchar(x%10+'0'); 21 } 22 void dfs(LL x){ 23 if(x>r) return; 24 if(x) s[ss++]=x; 25 dfs(x*10+6); 26 dfs(x*10+8); 27 } 28 inline LL gcd(LL x,LL y){return y?gcd(y,x%y):x;} 29 void get_ans(LL k,LL x,LL y){ 30 if(x>r) return; 31 if(k) ans+=(k&1)?r/x-(l-1)/x:(l-1)/x-r/x; 32 for(LL i=y;i<ss;i++){ 33 LL t=x/gcd(x,s[i]); 34 if(t<=r/s[i]) get_ans(k+1,t*s[i],i+1); 35 } 36 } 37 int main(){ 38 freopen("luckynumber.in","r",stdin); 39 freopen("luckynumber.out","w",stdout); 40 l=read(),r=read(); 41 dfs(0); 42 sort(s,s+ss); 43 for(LL i=0;i<ss;i++){ 44 for(LL j=0;j<fss;j++) 45 if(s[i]%fs[j]==0){s[i]=0;break;} 46 if(s[i]) fs[fss++]=s[i]; 47 } 48 ss=fss; 49 for(int i=0;i<ss;i++) s[i]=fs[ss-i-1]; 50 get_ans(0,1,0); 51 if(!ans) putchar('0'); 52 write(ans); 53 putchar(' '); 54 return 0; 55 }
bzoj | 74 | 1950429 | J_william |
872 KB | 672 MS | 1188 B | 2017-03-26 18:04:28 |
LONG LONG 真恶心!
题目来源:洛谷,bzoj