题目背景
四川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]内“近似幸运号码”的个数
输入输出样例
说明
对于30%的数据,保证1<=a<=b<=1000000
对于100%的数据,保证1<=a<=b<=10000000000
解题思路:
(暴力都没写对,还是细心的问题。)
观察可知,可以预处理出所有的幸运数字,不超过5000个
然后枚举对应范围内的合法数字,但是发现会有重复。怎们办呢?
对于某个幸运数字,如果他是另一个幸运数字的倍数,就删去他(因为枚举另一个幸运数字的倍数时一定会枚举到他)。
对于剩下的大约n==2000个幸运数字,搜索得到答案。
从n个数字中选取y个数字,最小公倍数为z,那么贡献为r/z-(l-1)/z;
但是依然会有重复,根据容斥原理(仔细想想):
就--> +1个的-2个的+3个的-4个的.......
(详见代码)
#include<iostream> #include<cstdio> #include<map> #include<algorithm> #define ll long long using namespace std; inline ll read() { ll x=0,w=1;char ch=getchar(); while(!isdigit(ch)){if(ch==‘-‘) w=-1;ch=getchar();} while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(); return x*w; } ll x,y,a[5000],k,ans,s[5000],p; bool fg[5000]; bool cmp(ll x,ll y){return x>y;} void dfs(int x,ll nw) { if(x==12) return; a[++k]=nw*10+6; dfs(x+1,a[k]); a[++k]=nw*10+8; dfs(x+1,a[k]); } ll gcd(ll x,ll y) { ll r=x%y; while(r) x=y,y=r,r=x%y; return y; } void DFS(int nw,int u,ll z) { if(nw>p) { if(u&1) ans+=y/z-(x-1)/z; else if(u) ans-=y/z-(x-1)/z; return; } DFS(nw+1,u,z);; ll tmp=z/gcd(s[nw],z); if((double)tmp*s[nw]<=y) DFS(nw+1,u+1,tmp*s[nw]); } int main() { dfs(1,0); for(int i=1;i<=k;++i) for(int j=1;j<=k;++j) if(j!=i && a[i]%a[j]==0) {fg[i]=1;break;} for(int i=1;i<=k;++i) if(!fg[i]) s[++p]=a[i]; sort(s+1,s+p+1,cmp); x=read();y=read(); DFS(1,0,1); printf("%lld",ans); return 0; }
交友须带三分侠气,作人要存一点素心 。