题目传送门:PAT-A-1010 Radix (25 分)
给出两个数n1,n2,还给出tag和radix,如果tag为1,则radix为n1的进制,如果tag=2,则radix为n2的进制,求另一个数在什么进制下与这个数相等。
首先:当tag=2时,swap(n1,n2)
此题有坑。我刚开始以为所求进制只是从2到36,因为输入的两个数中只有0-z,分别代表基数0-35,然鹅,提交了好多次,总是有四组数据过不了???,最后我发现题目并没有明确说明所求进制是36以内的。按理说进制就可能无限大,那么我们要怎么求出最小可能的进制呢。
我们可以分析一下,题目说了n1和n2长度不超过10,那么最大的数就是10个z,而10个z这种数至少是36进制,因为每一位数字不能超过其进制的基数。
36^10=3656158440062976,这个数已经超过了32int范围,但没有超过64位整数long long范围,我们可以是用long long 来存储十进制的值。
想要找到最小满足条件的进制,我们可以使用二分查找,二分的最小范围是n2这个数中最大位加1(也就是最小可能的进制数),二分的最大范围是n2这个数最大位加1的值与n1的十进制值的最大值。
然后进行常规二分操作即可。由于我二分写的不好,所以我每次在找到一个解的时候就记录下来,找到更小的就更新它,这样子我觉得会简单一点。当然那些代码写的好的人,直接用left记录最小解,不过我这样写经常会死循环调试半天,索性多花个变量记住解反而省事~
#include
using namespace std;
template<typename T = int>
T read(){
T x;
cin >> x;
return x;
}
typedef unsigned long long ll;
ll getRadix(string s, int radix){
ll ret = 0;
for(int i = 0; i < s.size(); i++){
int t;
if(s[i] >= 'a'){
t = s[i] - 'a' + 10;
}else{
t = s[i] - '0';
}
ret = 1LL*ret * radix + t;
}
return ret;
}
int main(){
freopen("input.txt", "r", stdin);
string n1 = read<string>(), n2 = read<string>();
int tag = read(), radix = read();
if(tag == 2) {
swap(n1, n2);
}
ll x = getRadix(n1, radix);
ll min_radix = 0;
for(auto i: n2){
if(isdigit(i)){
if(i - '0' > min_radix)
min_radix = i - '0' + 1;
}else{
if(i - 'a' + 10 > min_radix)
min_radix = i - 'a' + 10 + 1;
}
}
ll left = min_radix, right = max(x, min_radix);
ll best = 0;
while(left <= right){
ll mid = (left + right ) >> 1;
ll t = getRadix(n2, mid);
if(t < 0 || t > x){
right = mid - 1;
}else{
left = mid + 1;
if(t == x){
best = mid;//更新最优解
}
}
}
if(best){
cout << best << endl;
}else
cout << "Impossible" << endl;
}