(数学题)洛谷 P2567 SCOI2010 幸运数字 题解
题意
在中国,很多人都把 666 和 888 视为是幸运数字!lxhgww 也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字 666 和 888 的那些号码,比如 686868,666666666,888888888 都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在 [1,100][1,100][1,100] 的区间内就只有 666 个(666,888,666666,686868,868686,888888),于是他又定义了一种“近似幸运号码”。lxhgww 规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如 121212,161616,666666666 都是“近似幸运号码”。
现在 lxhgww 想知道在一段闭区间 [a,b][a, b][a,b] 内,“近似幸运号码”的个数。
1≤a≤b≤10101\le a\le b\le10^{10}1≤a≤b≤1010
思路
组成的数字只有 666 和 888,发现幸运数字并不多大概只有 200020002000 个。因此先找 bbb 以内的幸运数字,用 dfs 绰绰有余,并把有倍数关系的幸运数字去重,只留下最小的那个(比如 8,88,8888,88,8888,88,888 只保留 888),放进数组 ttt。
下面就要找近似幸运数了。做过小奥题的同学们都知道:
- 在 [1,n][1,n][1,n] 中,找 a,b,ca,b,ca,b,c 的倍数有几个,先分别找 a,b,ca,b,ca,b,c 的倍数个数,总和记为 s1s_1s1,然后减去分别找 lcm(a,b),lcm(b,c),lcm(a,c)lcm(a,b),lcm(b,c),lcm(a,c)lcm(a,b),lcm(b,c),lcm(a,c) 的倍数个数,总和为 s2s_2s2,最后再加上 lcm(a,b,c)lcm(a,b,c)lcm(a,b,c) 的倍数个数 s3s_3s3,就能算出答案。
- 求 [l,r][l,r][l,r] 内有多少个 kkk 的倍数,答案是 ⌊rk⌋−⌈lk⌉+1\left\lfloor\frac{r}{k}\right\rfloor-\left \lceil \frac{l}{k}\right \rceil+1⌊kr⌋−⌈kl⌉+1
其实就是 容斥定理 和 前缀思想。
对于第一个结论,形式化地书写就是:
考虑一个序列 {ttot}\{t_{tot}\}{ttot},记 valcntval_{cnt}valcnt 表示 {ttot}\{t_{tot}\}{ttot} 中所有的“任意 cntcntcnt 个元素的最小公倍数”。令 z(k)=⌊bk⌋−⌈ak⌉+1z(k)=\left\lfloor\frac{b}{k}\right\rfloor-\left \lceil \frac{a}{k}\right \rceil+1z(k)=⌊kb⌋−⌈ka⌉+1,那么:ans=∑i=1tot(−1)iz(vali)ans=\sum_{i=1}^{tot}(-1)^iz(val_i)ans=i=1∑tot(−1)iz(vali)
考虑到 b≤1010b\le 10^{10}b≤1010,算 lcmlcmlcm 的时候有相乘操作,开 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;
}
更多推荐



所有评论(0)