scoi2010 幸运数字
P2567 [SCOI2010]幸运数字
https://luogu.lohu.info/problem/show?pid=2567
题目描述
在中国,很多人都把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
容斥原理
直接计算会TLE,因为lcm会爆掉long long
所以2个优化:
1、幸运号码中,若i是j的倍数,去掉i
2、最重要的:计算lcm时,用类型double,判断lcm是否会超过b,不超过b才运算
double:负值取值范围为 -1.7976E+308 到 -4.94065645841246544E-324,正值取值范围为 4.94065645841246544E-324 到 1.797693E+308
#include<cstdio> #include<iostream> #include<algorithm> #define LL long long using namespace std; LL a,b,ans,g[10000],lu[10000],tot; int cnt; bool v[10000]; LL pre() { g[1]=6;g[2]=8; int h; for(h=1,cnt=2;g[h]<=b;h++) { g[++cnt]=g[h]*10+6; g[++cnt]=g[h]*10+8; while(g[cnt]>b) cnt--; } for(int i=1;i<=cnt;i++) if(!v[i]) { lu[++tot]=g[i]; for(int j=i+1;j<=cnt;j++) if(g[j]%g[i]==0) v[j]=true; } } LL gcd(LL x,LL y) { return y ? gcd(y,x%y):x; } inline void dfs(int now,LL num,int sum) { if(now==0) { LL s=b/num-a/num; if(sum&1) ans-=s; else ans+=s; return; } dfs(now-1,num,sum); double tmp=num/gcd(num,lu[now]); //注意这里不能写成 double tmp=num/gcd(num,lu[now])*lu[now],然后if(tmp<=b) //因为num/gcd(num,lu[now])仍是long long类型,再乘可能爆了 if(tmp*lu[now]<=b) dfs(now-1,num/gcd(num,lu[now])*lu[now],sum+1); } int main() { cin>>a>>b; pre(); --a; ans=b-a;//为什么ans初值不是0 //因为在dfs时,会dfs到一个数都不选,此时lcm=1,ans要减去 b/1-a/1,所以这里提前加上 dfs(tot,1,1); cout<<ans; }
想到了容斥原理,但没想到要去掉倍数
想到了lcm会爆long long,要用高静,弃疗了,没想到用double先判断,超过b的不运算