题意
在中国,很多人都把 6 6 6 和 8 8 8 视为是幸运数字!lxhgww 也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字 6 6 6 和 8 8 8 的那些号码,比如 68 68 68, 666 666 666, 888 888 888 都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在 [ 1 , 100 ] [1,100] [1,100] 的区间内就只有 6 6 6 个( 6 6 6, 8 8 8, 66 66 66, 68 68 68, 86 86 86, 88 88 88),于是他又定义了一种“近似幸运号码”。lxhgww 规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如 12 12 12, 16 16 16, 666 666 666 都是“近似幸运号码”。
现在 lxhgww 想知道在一段闭区间 [ a , b ] [a, b] [a,b] 内,“近似幸运号码”的个数。
1 ≤ a ≤ b ≤ 1 0 10 1\le a\le b\le10^{10} 1≤a≤b≤1010
思路
组成的数字只有 6 6 6 和 8 8 8,发现幸运数字并不多大概只有 2000 2000 2000 个。因此先找 b b b 以内的幸运数字,用 dfs 绰绰有余,并把有倍数关系的幸运数字去重,只留下最小的那个(比如 8 , 88 , 888 8,88,888 8,88,888 只保留 8 8 8),放进数组 t t t。
下面就要找近似幸运数了。做过小奥题的同学们都知道:
- 在 [ 1 , n ] [1,n] [1,n] 中,找 a , b , c a,b,c a,b,c 的倍数有几个,先分别找 a , b , c a,b,c a,b,c 的倍数个数,总和记为 s 1 s_1 s1,然后减去分别找 l c m ( a , b ) , l c m ( b , c ) , l c m ( a , c ) lcm(a,b),lcm(b,c),lcm(a,c) lcm(a,b),lcm(b,c),lcm(a,c) 的倍数个数,总和为 s 2 s_2 s2,最后再加上 l c m ( a , b , c ) lcm(a,b,c) lcm(a,b,c) 的倍数个数 s 3 s_3 s3,就能算出答案。
- 求 [ l , r ] [l,r] [l,r] 内有多少个 k k k 的倍数,答案是 ⌊ r k ⌋ − ⌈ l k ⌉ + 1 \left\lfloor\frac{r}{k}\right\rfloor-\left \lceil \frac{l}{k}\right \rceil+1 ⌊kr⌋−⌈kl⌉+1
其实就是 容斥定理 和 前缀思想。
对于第一个结论,形式化地书写就是:
考虑一个序列 { t t o t } \{t_{tot}\} {ttot},记 v a l c n t val_{cnt} valcnt 表示 { t t o t } \{t_{tot}\} {ttot} 中所有的“任意 c n t cnt cnt 个元素的最小公倍数”。令 z ( k ) = ⌊ b k ⌋ − ⌈ a k ⌉ + 1 z(k)=\left\lfloor\frac{b}{k}\right\rfloor-\left \lceil \frac{a}{k}\right \rceil+1 z(k)=⌊kb⌋−⌈ka⌉+1,那么: a n s = ∑ i = 1 t o t ( − 1 ) i z ( v a l i ) ans=\sum_{i=1}^{tot}(-1)^iz(val_i) ans=i=1∑tot(−1)iz(vali)
考虑到 b ≤ 1 0 10 b\le 10^{10} b≤1010,算 l c m lcm lcm 的时候有相乘操作,开 long long 数据恶心点就爆,那就要用 long long 2:__int128 来定义。(就因为这个虚空调试了很久)
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define dd double
#define i8 __int128
const ll N=2e6+9;
ll a,b;
ll tot,n;
ll t[N],xy[N];
bool vis[N];
void dfs(ll x)
{
if(x>b)return;
if(x)xy[++n]=x;
dfs(x*10+6);
dfs(x*10+8);
}
bool cmp(ll x,ll y)
{
return x>y;
}
ll ans;
i8 gcd(i8 x,i8 y)
{
if(y==0)return x;
if(x==0)return y;
return gcd(y,x%y);
}
i8 lcm(i8 x,i8 y)
{
if(x==0)return y;
if(y==0)return x;
return x*y/gcd(x,y);
}
void cal(ll id,ll cnt_lcm,i8 val)
{
if(val>b)return;
if(id>tot)
{
if(val)
{
ll qj=(ll)(cnt_lcm&1?1:-1);
ans+=qj*(floor((dd)b/val)-ceil((dd)a/val)+1);
}
return;
}
cal(id+1,cnt_lcm+1,lcm(val,t[id]));
cal(id+1,cnt_lcm,val);
}
int main()
{
scanf("%lld%lld",&a,&b);
dfs(0);
sort(xy+1,xy+n+1);
for(int i=1;i<=n;i++)
{
if(!vis[i])t[++tot]=xy[i];
for(int j=i+1;j<=n;j++)
if(xy[j]%xy[i]==0)vis[j]=1;
}
sort(t+1,t+tot+1,cmp);
cal(1,0,0);
printf("%lld",ans);
return 0;
}