容斥原理。
记[l,r]内6的倍数的数的个数为集合S1,8的倍数的为集合S2,66的倍数为集合S3,68的倍数为集合S4......那么答案就是所有这些集合的并集大小。
发现幸运号码并不会很多,最多也就21+22+...+210=211−2=2046个,可以用容斥原理解决,但需要一些优化。
先筛去一些是已有数的倍数的数,如因为66是6的倍数,所以66倍数的集合肯定包含于6倍数的集合,可以直接不考虑66。这样会只剩下943个。
搜索的时候从数字大的数开始搜索,以减少回溯层数,否则TLE。
求LCM的时候可能爆longlong,要用double- -
时间复杂度O(玄学)…
#include<cstdio>
#include<algorithm>
using namespace std;
const double eps = 1e-3;
int m;
long long l, r, a[3000], ans;
bool vis[3000];
void pre(long long x)
{
if(x>r)return;
a[++m]=x;
pre(x*10+6);
pre(x*10+8);
}
bool cmp(long long a,long long b){return a>b;}
long long gcd(long long a, long long b){return b==0?a:gcd(b,a%b);}
void dfs(int now, int cnt, long long x)
{
if(now>m)
{
if(cnt&1)ans+=r/x-(l-1)/x;
else if(cnt)ans-=r/x-(l-1)/x;
return;
}
long long tmp=x/gcd(a[now],x);
dfs(now+1,cnt,x);
if((double)a[now]*tmp<=r+eps)
dfs(now+1,cnt+1,tmp*a[now]);
}
int main()
{
scanf("%lld%lld",&l,&r);
pre(6);pre(8);
sort(a+1,a+1+m,cmp);
for(int i = m; i >= 1; i--)
if(!vis[i])
for(int j = i-1; j >=1; j--)
if(a[j]%a[i]==0)
vis[j]=1;
int tmp=0;
for(int i = 1; i <= m; i++)
if(!vis[i])
a[++tmp]=a[i];
m=tmp;
dfs(1,0,1);
printf("%lld\n",ans);
}