题意:
所有只含6与8的数叫做幸运数字,幸运数字的倍数叫做近似幸运数字,幸运数字都是近似幸运数字。
给定区间[l,r]求其中近似幸运数字个数。
1 < =l< =r< =10000000000
解析:
显然容斥,剩下的就是黑科技的问题了。
容斥的话,就是我们首先预处理出来所有的幸运数字,然后筛一遍,筛掉所有是其他幸运数字倍数的数避免重复计算。
然后就是选1个-选2个的lcm+选3个的lcm-….
一个dfs搞定即可。
但是需要注意,dfs内部的lcm我们不可以求出来,因为会爆long long ,是的我也不知道为什么。
所以我们需要转成double 型比较是否越界。
另外,我们筛完之后的那些数字最好按照从大到小的顺序排,如果从小到大的话常数大一点点,但就是这么一点点你就过不去,貌似是从大到小的话更能够对一些非法状态早排除吧。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 10010
using namespace std;
typedef long long ll;
ll l,r;
int cnt;
int n;
ll lucky[N];
bool ban[N];
ll ans;
ll gcd(ll a,ll b)
{
while(b)
{
ll t=b;
b=a%b;
a=t;
}
return a;
}
ll lcm(ll a,ll b)
{
return a/gcd(a,b)*b;
}
void init(ll num)
{
if(num>r)return;
lucky[cnt++]=num;
init(num*10+6);
init(num*10+8);
}
void dfs(int now,int chose,ll val)
{
if(now>n)
{
if(!chose)return;
if(chose&1)ans+=r/val-(l-1)/val;
else ans-=r/val-(l-1)/val;
return;
}
dfs(now+1,chose,val);
ll tmp=val/gcd(lucky[now],val);
if((double)tmp*lucky[now]<=r)
{
dfs(now+1,chose+1,tmp*lucky[now]);
}
}
int main()
{
scanf("%lld%lld",&l,&r);
init(0);
cnt--;
sort(lucky+1,lucky+cnt+1);
for(int i=1;i<=cnt;i++)
{
if(!ban[i])
{
for(int j=i+1;j<=cnt;j++)
{
if(lucky[j]%lucky[i]==0)
{
ban[j]=1;
}
}
}
}
for(int i=1;i<=cnt;i++)
if(!ban[i])lucky[++n]=lucky[i];
for(int i=1;i<=(n>>1);i++)swap(lucky[i],lucky[n-i+1]);
dfs(1,0,1);
printf("%lld\n",ans);
}