第一眼以为是数位dp
但看到要求只能有6和8并且对每位上的数没什么要求
于是想到容斥
ans=选1个-选2个的lcm+选3个的lcm···
容斥的实现通常结合了dfs
先预处理出1-r范围内所有的真幸运数字
然后保留比r/2小的,也就是说比r/2大的都可以用这些来覆盖到
而且最后要从大到小排个序,使得lcm更快达到上界r
然后就是dfs啦,要记录当前是选几个,选到了哪里以及当前算出的lcm
(下面一段是hzwer的)
此题数据范围是真正的10^10,于是我们需要一些方法来防止爆long long
就是这段
if(((double)a[x]*tmp)<=r) dfs(x+1,y+1,a[x]*tmp);
因为a[x]*tmp可能超过long long,于是我们先用double存它的近似值,如果它小于r,则一定在long long范围内,就可以直接乘了
具体细节看注释
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
#define maxn 10005
using namespace std;
LL l,r,a[maxn],b[maxn],ans;
int numa,numb;
bool hs[maxn];
void init(int x,LL y){
if(y>r) return;
if(x) a[++numa]=y;
init(x+1,y*10+6);
init(x+1,y*10+8);
}
LL gcd(LL x,LL y) {return y==0?x:gcd(y,x%y);}
void dfs(int now,int ty,LL x){
if(now>numb){
if(ty&1) ans+=r/x-(l-1)/x;//容斥
else if(ty) ans-=r/x-(l-1)/x;
return;
}
dfs(now+1,ty,x);
LL tmp=x/gcd(a[now],x);
if((double)a[now]*tmp<=r)//当前lcm比r大就不再搜
dfs(now+1,ty+1,a[now]*tmp);
}
int main(){
scanf("%lld%lld",&l,&r);
init(0,0);//预处理1-r的真幸运数字
sort(a+1,a+numa+1);
for(int i=1;i<=numa;i++)
if(!hs[i]){
b[++numb]=a[i];
for(int j=i+1;j<=numa;j++)//舍弃能被前面的数覆盖的
if(a[j]%a[i]==0) hs[j]=1;
}
for(int i=1;i<=numb;i++)
a[numb-i+1]=b[i];//预处理出的幸运数字从大到小排序,使lcm能更快达到r的上界
dfs(1,0,1);
printf("%lld\n",ans);
return 0;
}