#搜索,容斥#洛谷 2567 [SCOI2010]幸运数字
题目
问区间\([l,r],l,r\leq 10^{10}\)中有多少个数是
数位由6或8组成的数的倍数(包括本身)
分析
数位由6或8组成的数最多有两千多种,
这可以直接一遍暴搜得到
对于区间\([1,n]\)答案应该是\(\frac{n}{i}-\frac{n}{i*j}+\frac{n}{i*j*k}\)
当选出的数的最小公倍数超过\(n\)可以剪枝,不会达到上界,
但是这样远远不够,考虑若\(i|j\)那么\(j\)显然可以直接舍弃,剩下的数可以降序排序后剪枝
然后对于超过\(n/2\)的数被选择只能选择自己(两数gcd至少为2),可以提前处理,这样就可以AC了
代码
#include <cstdio>
#include <algorithm>
#define rr register
using namespace std;
typedef long long lll; bool v[2111];
lll b[2111],l,r,ans,sum,now,m;
inline void Pro(lll now,lll n){
if (now>n) return;
b[++b[0]]=now;
Pro(now*10+6,n),
Pro(now*10+8,n);
}
inline lll gcd(lll x,lll y){return y?gcd(y,x%y):x;}
inline void dfs(int dep,lll lcm,int cnt){
if (dep>m) return;
dfs(dep+1,lcm,cnt);
rr lll t=lcm*b[dep]/gcd(lcm,b[dep]);
if (t>now) return;
if (cnt&1) ans-=now/t;
else ans+=now/t;
dfs(dep+1,t,cnt+1);
}
inline lll answ(lll n){
if (!n) return 0;
ans=0,now=n,dfs(1,1,0);
return ans;
}
signed main(){
scanf("%lld%lld",&l,&r),Pro(6,r),Pro(8,r);
sort(b+1,b+1+b[0]);
for (rr int i=1;i<=b[0];++i) if (!v[i])
for (rr int j=i+1;j<=b[0];++j) if (b[j]%b[i]==0) v[j]=1;
for (rr int i=1;i<=b[0];++i)
if (!v[i]){
if (b[i]>r/2) sum+=r/b[i]-(l-1)/b[i];
else b[++m]=b[i];
}
reverse(b+1,b+1+m);
return !printf("%lld",sum+answ(r)-answ(l-1));
}