解题报告:一道关于进制转换的题

Problem

给定一个十进制数 y y ,请选择一个进制b,使得 y y 转成b进制后大于或等于 b b 进制下的l,且每位均为 0 0 9之间的数码。求最大的 b b

数据范围:10ly1018

Analysis

乍看起来是道很简单的题。 (y)10(l)b ( y ) 10 ≥ ( l ) b 有两种考虑方向,可以把 l l b进制解析,得到 ilibiy ∑ i l i b i ≤ y 。这样左边对 b b 是单调的,可以考虑二分。

另一种方向是把y化成 b b 进制来与l比较,设为 m+1 m + 1 位数 a a 。这样就是要找一个al满足 mi=0aibi=y ∑ i = 0 m a i b i = y 。左边 ambm a m b m 比其余项加起来还大很多,这样可以给 b b 很强的限制。

但这只考虑了一条主要限制,细想之下,另一条限制更加棘手,即y b b 进制下每位均在0 9 9 之间。这使得大部分b都不合法,并且分布方式难以捉摸,答案可能离满足 (y)10(l)b ( y ) 10 ≥ ( l ) b 的最大的 b b 相去甚远,因此前一种思路告吹。

所以关键在于解这样一个式子

{a0b0+a1b1++ambm=y0ai9al

这里 10y,l,b,a1018 10 ≤ y , l , b , a ≤ 10 18 ,因此 1m18 1 ≤ m ≤ 18

Solution 1

a0 a 0 移到右边,得 b|(ya0) b | ( y − a 0 ) ,因此把 y,y1,,y9 y , y − 1 , ⋯ , y − 9 这10个数用Pollard-Rho分解(其实我不会写rho算法),枚举一个约数 b b 来check。

check就是暴力把y转成 b b 进制,单次复杂度logbylgy

Solution 2

由于 bm1018 b m ≤ 10 18 ,所以值得考虑的 b b 的分布是极不均匀的。可以在b小时枚举 b b ,在b大时 m m 很小从而可以枚举a。例如枚举完 a<104 a < 10 4 后,剩下的一定有 m4 m ≥ 4 ,于是 b10184<31623 b ≤ 10 18 4 < 31623

现在给定一个 a a ,如何判断是否存在b使 y=(a)b y = ( a ) b 。我们发现 ambmy<(am+1)bm a m b m ≤ y < ( a m + 1 ) b m 并不能给出足够强的限制,因为当 m=1,am=1 m = 1 , a m = 1 时它给出的区间可以长达 1018/2 10 18 / 2

但是这里用关于 b b 的单调性来二分是没问题的。复杂度约为10000loglg+31613lg

Solution 3

ambmy<(am+1)bm a m b m ≤ y < ( a m + 1 ) b m 不足以给出足够强的限制,其实是因为我们太粗犷了。式中认为 ai<b a i < b ,但我们知道更强的限制是 ai9 a i ≤ 9 。所以重新列出式子:

ambmyambm+9×bm1b1 a m b m ≤ y ≤ a m b m + 9 × b m − 1 b − 1

看来 b b 确实只能在y/amm很近的地方,可是如何严谨地证明这一点呢。式中 b b bm同时出现,不大能解。但是我们只需要估算就行了。

不等式的左边很好解, by/amm b ≤ y / a m m ,即 bmax=y/amm b m a x = ⌊ y / a m m ⌋ b b 的上界。我们可以把右边分式部分中的b换成 bmax b m a x ,这样合法是因为相当于把 a a 末尾一串9999的权值放大。这样就可以把b的范围严谨地解出来了:

y9×bmmax1bmax1ammbyamm y − 9 × b m a x m − 1 b m a x − 1 a m m ≤ b ≤ y a m m

可见在 b b 较大时,这个上下界的距离很近,几乎是常数(如果有人会算这东西到底有多小,跪求大佬指点)。枚举这个范围内的b来check

b100 b ≤ 100 的暴力,再枚举 1m8,0am9 1 ≤ m ≤ 8 , 0 ≤ a m ≤ 9 计算。复杂度大约不超过 300lg 300 lg

Code

#include <cmath>
#include <cstdio>
typedef long long ll;
inline ll and10(ll l) {return l > 10 ? l : 10;}

ll calc(ll y, ll b) {
    ll a = 0, p = 1;
    do{ ll t = y % b;
        if (t > 9) return -1;
        a += t * p;
        p *= 10;
    } while (y /= b);
    return a;
}

int main() {
    ll y, l;
    scanf("%lld%lld",&y,&l);
    for (int m = log10l(l); m < 9; m++)
        for (int a = 1; a <= 9; a++)
            for (ll br = powl(y / a + 1, 1.0L/m),
                bl = and10(powl((y - (powl(br, m) - 1) / (br - 1) * 9) / a, 1.0L/m));
                br >= bl; br--) if (calc(y, br) >= l) {printf("%lld\n",br); return 0;}
    int b = 100;
    while (calc(y, b) < l) b--;
    printf("%d\n",b);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值