bzoj1853 [Scoi2010]幸运数字

本文介绍了一个有趣的数学问题:如何找出一个区间内所有由特定数字组成的幸运号码及其倍数(近似幸运号码)。文章提供了详细的算法实现,包括搜索所有幸运号码并找到它们的倍数的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://www.elijahqi.win/archives/3883
Description
在中国,很多人都把6和8视为是幸运数字!lxhgww也这样认为,于是他定义自己的“幸运号码”是十进制表示中只包含数字6和8的那些号码,比如68,666,888都是“幸运号码”!但是这种“幸运号码”总是太少了,比如在[1,100]的区间内就只有6个(6,8,66,68,86,88),于是他又定义了一种“近似幸运号码”。lxhgww规定,凡是“幸运号码”的倍数都是“近似幸运号码”,当然,任何的“幸运号码”也都是“近似幸运号码”,比如12,16,666都是“近似幸运号码”。 现在lxhgww想知道在一段闭区间[a, b]内,“近似幸运号码”的个数。
Input
输入数据是一行,包括2个数字a和b
Output
输出数据是一行,包括1个数字,表示在闭区间[a, b]内“近似幸运号码”的个数
Sample Input
【样例输入1】
1 10
【样例输入2】
1234 4321
Sample Output
【样例输出1】
2
【样例输出2】
809
HINT
【数据范围】
对于30%的数据,保证1 < =a < =b < =1000000
对于100%的数据,保证1 < =a < =b < =10000000000

Source
Day1

可以发现其实就是 先搜索出所有的数 然后找他们的倍数即可

我们发现两两的lcm被多算了 然后减去 又发现三个的lcm多减了 所以加上

就这样 这题数据比较强 需要剪枝 就是double 那个乘起来大于右边界的时候结束即可

注意需要将原搜索序列翻转 效果更好

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
ll a[N],l,r,ans;bool vis[N];int m,n;
inline void init(int x,ll v){
    if (v>r) return;if (x) a[++m]=v;
    init(x+1,v*10+6);init(x+1,v*10+8);
}
inline ll gcd(ll x,ll y){
    return !y?x:gcd(y,x%y);
}
inline void dfs(int x,int nm,ll v){
    if (x>n){
        if(v==1) return;int op=nm&1?1:-1;
        ans+=r/v*op;ans-=(l-1)/v*op;return;
    }dfs(x+1,nm,v);
    ll g=v/gcd(a[x],v);
    if((double)g*a[x]<=r) dfs(x+1,nm+1,g*a[x]);
}
int main(){
    //freopen("bzoj1853.in","r",stdin);
    scanf("%lld%lld",&l,&r);
    init(0,0);sort(a+1,a+m+1);
    for (int i=1;i<=m;++i){
        if (vis[i]) continue;
        for (int j=i+1;j<=m;++j) if (a[j]%a[i]==0) vis[j]=1;
        a[++n]=a[i];
    }reverse(a+1,a+n+1);
    dfs(1,0,1);printf("%lld\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值