题目
题目背景
我昨天还以为,
H
a
n
d
I
n
D
e
v
i
l
\sf HandInDevil
HandInDevil 是 日象 家的天才,不可能有人战胜它的 马桶白眼;今天的
D
D
(
X
Y
X
)
\sf DD(XYX)
DD(XYX) 的洞见就超越了它。他的体内,有着比人类更高明、更强大之物,炎翼鸟 也只是祂的宠物——祂是,普林斯普!
“你的那双眼睛,比我的 马桶白眼 更厉害,” 质朴的 H a n d I n D e v i l \sf HandInDevil HandInDevil 浮现在 D D ( X Y X ) \sf DD(XYX) DD(XYX) 眼前,“现在 O n e I n D a r k \sf OneInDark OneInDark 正身处光明之中,而你能将他打入黑暗。”
题目描述
给出十进制数
y
y
y 和
b
b
b 进制数
l
l
l,保证
l
l
l 的数码都在
[
0
,
9
]
[0,9]
[0,9] 中。你需要选择
b
b
b 使得
y
(
10
)
⩾
l
(
b
)
y_{(10)}\geqslant l_{(b)}
y(10)⩾l(b) 且
y
(
b
)
y_{(b)}
y(b) 的每一位都在
[
0
,
9
]
[0,9]
[0,9] 之中。求最大的
b
b
b 。这里
x
(
m
)
x_{(m)}
x(m) 表示将
x
x
x 的十进制表示
x
=
∑
i
⩾
0
a
i
1
0
i
(
a
i
∈
[
0
,
9
]
)
x=\sum_{i\geqslant 0} a_i10^i\;(a_i\in[0,9])
x=∑i⩾0ai10i(ai∈[0,9]) 重写为
m
m
m 进制下
x
(
m
)
=
∑
i
⩾
0
a
i
m
i
x_{(m)}=\sum_{i\geqslant 0}a_im^i
x(m)=∑i⩾0aimi 。
数据范围与约定
为了避免某些问题,保证
10
⩽
l
⩽
y
⩽
1
0
18
10\leqslant l\leqslant y\leqslant 10^{18}
10⩽l⩽y⩽1018 。数据组数
T
⩽
7
×
1
0
4
T\leqslant 7\times 10^4
T⩽7×104 。
思路
乍看之下,好像 y ⩾ l ( b ) y\geqslant l_{(b)} y⩾l(b) 是很简单的,直接二分就得到了 b b b 的范围。然而下一个问题是:每个数位都在 [ 0 , 9 ] [0,9] [0,9] 该怎么翻译?
难以数位
d
p
\tt dp
dp 的时候,不妨从数学的角度考虑。第
k
k
k 位上的数其实就是
⌊
y
b
k
⌋
−
b
⌊
y
b
k
+
1
⌋
⩽
9
\left\lfloor{y\over b^k}\right\rfloor-b\left\lfloor{y\over b^{k+1}}\right\rfloor\leqslant 9
⌊bky⌋−b⌊bk+1y⌋⩽9
只能想到整除分块,对于 y ⩾ b 4 y\geqslant b^4 y⩾b4 就用上式找到合法的 b b b,而 y < b 4 y<b^4 y<b4 就直接枚举 y ( b ) y_{(b)} y(b) 数位。时间复杂度较高。
优化点在于,可行的 b b b 其实非常非常少。上式中,很多整除分块的结果都是解不出一个合法的 b b b 的。我们只要能够充分剪枝就成功了。
第一个想法是,最高位的限制最强,所以我们要求
b
k
b^k
bk 是严格最高位。但是这还不够强。问题还是在整除分块上——所以要大胆弃用原方法,转而 枚举该数位值。设为
a
a
a,则
a
b
k
⩽
y
⩽
a
b
k
+
9
∑
i
=
0
k
−
1
b
i
ab^k\leqslant y\leqslant ab^k+9\sum_{i=0}^{k-1}b^i
abk⩽y⩽abk+9i=0∑k−1bi
显然
b
⩾
10
b\geqslant 10
b⩾10 且
k
⩾
1
k\geqslant 1
k⩾1 。我们会惊讶地发现,可行的
b
b
b 的数量 少得可怜。因为后面简单放缩(或者直接用
b
b
b 进制数思考)可以发现
y
⩽
a
b
k
+
10
b
k
−
1
y\leqslant ab^k+10b^{k-1}
y⩽abk+10bk−1
也就是让它再进一位。那么,若找到最小的 b b b 使得原不等式成立,则 a ⋅ ( b + δ ) k ⩾ a b k + a k δ b k − 1 a\cdot(b+\delta)^k\geqslant ab^k+ak\delta b^{k-1} a⋅(b+δ)k⩾abk+akδbk−1,在 δ ⩾ 10 a k \delta\geqslant{10\over ak} δ⩾ak10 时就会超过 a b k + 10 b k − 1 ab^k+10b^{k-1} abk+10bk−1 即 y y y 的上界,所以可行的 b b b 的数量只有 O ( δ ) = O ( 10 a k ) \mathcal O(\delta)=\mathcal O({10\over ak}) O(δ)=O(ak10) 个!
于是只需要找到 b m a x = y a k b_{max}=\sqrt[k]{y\over a} bmax=kay,然后暴力判断 b ∈ [ b m a x − δ , b m a x ] b\in[b_{max}{\rm-}\delta,b_{max}] b∈[bmax−δ,bmax] 是否为解。时间复杂度几乎是 O ( 10 log 10 l ln y ) \mathcal O(10\log_{10}l\ln y) O(10log10llny) 的。
代码
即使分析出来很正确,还是需要剪枝卡常,这就很不厚道了……
#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
#include <cmath>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
# define double (long double)
inline llong readint(){
llong a = 0; int c = getchar(), f = 1;
for(; !isdigit(c); c=getchar())
if(c == '-') f = -f;
for(; isdigit(c); c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(const llong &x){
if(x > 9) writeint(x/10);
putchar(int((x%10)^48));
}
bool check(llong y, const llong &l, const llong &b){
llong all = 0, v = 1;
for(int d; y; y/=b){
if((d = int(y%b)) > 9) return false;
all += d*v, v *= 10;
}
return all >= l; // satisfying
}
inline llong qkpow(llong b, int q){
llong a = 1; // no modules
for(; q; q>>=1,b*=b) if(q&1) a *= b;
return a;
}
llong powten[19];
int main(){
rep(i,int(powten[0]=1),18) powten[i] = 10*powten[i-1];
for(int T=int(readint()); T; --T){
llong y = readint(), l = readint();
for(int k=1; powten[k<<1]<y; ++k) rep(a,1,9){
if(a+1 <= (l-1)/powten[k]) continue;
llong b = llong(round(pow(double(y)/a,double(1)/k)+1));
llong lb = llong(round(pow((y-(qkpow(b,k)
-1)/(b-1)*9)/a,double(1)/k)-1));
for(lb=max(lb,9ll); b!=lb; --b) // bottom line
if(check(y,l,b)){ writeint(b); goto SCHEME; }
}
for(int b=100; true; --b) // bruteforce
if(check(y,l,b)){ writeint(b); break; }
SCHEME: putchar('\n');
}
return 0;
}
后记
据称,想到这个做法的原因是,一个数字的最高位对数字大小的影响最大。或许与本题有些相似,或许没有。