m a t h r m p r o b l e m mathrm{problem} mathrmproblem
给定
n
n
n和
s
s
s,求满足条件的最小
b
b
b.
S o l u t i o n \mathrm{Solution} Solution
这道题的分类比较奇妙。观察到所有数据的范围比较奇妙,是 1 0 11 10^{11} 1011,很适合复杂度为根号的算法。如若复杂度不是 n \sqrt n n,那么可以将数据范围开到 1 0 18 10^{18} 1018级别。
同时,我们发现函数 f ( b , n ) f(b,n) f(b,n)的所用为:
- 求解n在b进制下各个数位的数字之和。
假设数字是两位数
x
y
xy
xy,那么就有
x
×
b
+
y
=
n
x\times b+y=n
x×b+y=n.此时有可能存在
n
<
b
\sqrt n < b
n<b
假设数字是三位数为
x
y
z
xyz
xyz,那么就有
x
×
b
2
+
y
×
b
+
c
=
n
x\times b^2+y\times b+c=n
x×b2+y×b+c=n,此时就不存在
n
<
b
\sqrt n < b
n<b.
因为要使 n < b \sqrt n < b n<b,那么数字就必须是两位数。一位数的情况需要特判。
然后,我们就可以将 b b b分类,当 b ≤ n b\le \sqrt n b≤n的时候我们选择跑暴力.
剩下的就是两位数的情况,我们可以得到: { x + y = s x × b + y = n \begin{cases} x + y = s \\x \times b + y = n\end{cases} {x+y=sx×b+y=n
然后通过两式相减就有: ( b − 1 ) × x = n − s (b-1)\times x=n-s (b−1)×x=n−s
然后就有: b = n − s x + 1 b=\frac{n-s}{x}+1 b=xn−s+1
然后我们就乱七八糟的枚举n-s的约数,判来判去即可。
代码如下:
#include <cmath>
#include <cstdio>
#include <iostream>
#define int long long
using namespace std;
int n, s;
int f(int b,int n)
{
if (n < b) return n;
return f(b,n/b) + n % b;
}
signed main(void)
{
cin>>n>>s;
if (n == s) return cout<<n+1,0;
for (int i=2;i<=(int)sqrt(n);++i)
if (f(i,n) == s) return cout<<i, 0;
int ans = 1e18;
for (int i=1;i*i<=n;++i)
if ((n - s) % i == 0) {
int b = (n - s) / i + 1;
int x = i, y = s - i;
if (x >= 0 && y >= 0 && b > sqrt(n) && x < b && y < b)
ans = min(ans,b);
}
cout<<(ans == 1e18 ? -1 : ans)<<endl;
return 0;
}