1853: [Scoi2010]幸运数字
Time Limit: 2 Sec Memory Limit: 64 MBSubmit: 2269 Solved: 829
[ Submit][ Status][ Discuss]
Description
在中国,很多人都把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和b
Output
输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数
Sample Input
【样例输入1】
1 10
【样例输入2】
1234 4321
1 10
【样例输入2】
1234 4321
Sample Output
【样例输出1】
2
【样例输出2】
809
2
【样例输出2】
809
HINT
【数据范围】
对于30%的数据,保证1 < =a < =b < =1000000
对于100%的数据,保证1 < =a < =b < =10000000000
Source
方法1:暴力容斥,在lcm>b时剪枝,复杂度O(玄学)
方法2:前若干个容斥(我选不超过100000的所有数),后面直接暴力,复杂度O(玄学)(比上面的低)
代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<map>
#define lbt(i) ((i)&(-(i)))
using namespace std;
typedef long long ll;
const int MX=20100,A=(1<<9)-1;
map<ll,bool>vis;
ll q[MX],s[MX];
ll ans,b,u;
int sc;
inline ll lcm(ll a,ll b)
{
ll res=a*b;
if(a<b)swap(a,b);
for(;b;b^=a^=b^=a%=b);
return res/a;
}
inline void dfs(ll chosen,ll k,int K)
{
if(k>u)return;
ans+=(K?(u/k-(b-1)/k):-(u/k-(b-1)/k));
for(ll i=1,j=1;i<lbt(chosen);i<<=1,j++)
dfs(chosen|i,lcm(k,s[j]),K^1);
}
ll calc()
{
int h=0,t=0;
q[++t]=6;q[++t]=8;
while(h<t)
{
ll r=q[++h];
if(r>u/100000){h--;break;}
q[++t]=r*10+6;
q[++t]=r*10+8;
bool flg=true;
for(int i=1;i<=sc;i++)
if(r%s[i]==0){flg=false;break;}
if(flg)s[++sc]=r;
}
for(int i=0;i<sc;i++)dfs(1LL<<i,s[i+1],1);
while(h<t)
{
ll r=q[++h];
if(r>u)break;
q[++t]=r*10+6;
q[++t]=r*10+8;
bool flg=true;
for(int i=1;i<=sc;i++)
if(r%s[i]==0){flg=false;break;}
if(flg){
for(ll tmp=(b-1)/r*r+r;tmp<=u;tmp+=r)
{
bool flg1=true;
for(int i=1;i<=sc;i++)
if(tmp%s[i]==0){flg1=false;break;}
if(flg1&&!vis[tmp]){
vis[tmp]=true;ans++;
}
}
}
}
return ans;
}
int main()
{
scanf("%lld%lld",&b,&u);
printf("%lld\n",calc());
return 0;
}