Given a pair of positive integers, for example, 6 and 110, can this equation 6 = 110 be true? The answer is yes, if 6 is a decimal number and 110 is a binary number.
Now for any pair of positive integers N 1 N_1 N1 and N 2 N_2 N2, your task is to find the radix of one number while that of the other is given.
Input Specification:
Each input file contains one test case. Each case occupies a line which contains 4 positive integers:
N1 N2 tag radix
Here N1
and N2
each has no more than 10 digits. A digit is less than its radix and is chosen from the set { 0-9, a
-z
} where 0-9 represent the decimal numbers 0-9, and a
-z
represent the decimal numbers 10-35. The last number radix
is the radix of N1
if tag
is 1, or of N2
if tag
is 2.
Output Specification:
For each test case, print in one line the radix of the other number so that the equation N1
= N2
is true. If the equation is impossible, print Impossible
. If the solution is not unique, output the smallest possible radix.
Sample Input 1:
6 110 1 10
Sample Output 1:
2
Sample Input 2:
1 ab 1 2
Sample Output 2:
Impossible
Caution:
这道题本来以为不难,但是25分一直只拿15、19分,让我卡了快半个晚上+一个中午,值得记一下。
卡我最关键的点是:进制不能只算到36。。。
因为题目中只说到了‘z’表示35,所以我想当然地以为进制只算到36就行了,但是不是这样的。。。
所以我之前的代码里面有一句
if(tmpRadix > 36) cout << "Impossible" << endl;
就是这一句导致我卡了好久,还是上网查这个题才发现不对劲的,知乎上这个回答说用二分,我还不以为然:“不就不超过40个可能嘛,没必要用二分吧”,仔细一看代码才发现不对。
然后把那个判断基数大于36的语句去掉后再提交,拿到24分了,剩下一分超时,好吧,我这就滚回去用二分。
用二分的时候还有两个很重要的点:
首先是right
的选取,还是看了上面知乎的那个分享,我自己想了一会后的理解如下:
现在我们有n1
的十进制表示,要找到一个合适的基数,使得n2
在对应进制下所表达的数转换成十进制后是n1
,
假如n2
是一个一位数,那么其实只要基数合理(这里的合理指的是基数大于n2
,就好比数字 7
不可能是七进制下的数字),则n2
所表示的数转换成十进制后都是一样的(如7
在8进制、18进制、28进制下表示的数都是十进制的7
);
假如n2
是一个多位数,那么 n1 + 1
进制下的一个多位数所能表达的最小的数字就是n1
(例如 8 进制下的一个多位数所能表达的最小的数字是10
,转换成十进制后就是8
),所以不需要再考虑n1 + 2
及以上的进制,因为这些进制下 n2
所能表示的最小的数也大于 n1
。
综上,我们要找的基数如果存在的话,一定不会超过n1 + 1
。
第二点需要注意的就是溢出问题,除了使用longlong类型存储n1
、n2
和mid
外,还有一点:下面代码里面被注释掉的两行是错误的,因为n2
即使是longlong,还是会在计算过程中存在溢出现象(因为我们用二分的时候可能某一次计算的 n2
特别特别大),就导致 n2 < 0
,如果遇到这种情况就说明我们要找的基数在mid
左边,但是注释掉的两行是在 else if(n2 < n1)
语句里处理这种情况的,反而把 left
右移了,所以会偏离正确答案。
Solution:
// Talk is cheap, show me the code
// Created by Misdirection 2021-08-10 17:47:40
// All rights reserved.
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
int main(){
string num1, num2;
long long n1 = 0, n2 = 0;
int tag, radix;
cin >> num1 >> num2;
cin >> tag >> radix;
if(tag == 2){
string tmp = num1;
num1 = num2;
num2 = tmp;
}
int len = num1.length();
for(int i = 0; i < len; ++i)
n1 += (isdigit(num1[i]) ? (num1[i] - '0') : (num1[i] - 'a' + 10)) * pow(radix, len - i - 1);
char maxDigit = *max_element(num2.begin(), num2.end());
int tmpRadix = isdigit(maxDigit) ? (maxDigit - '0' + 1) : (maxDigit - 'a' + 11);
long long left = tmpRadix, right = n1 + 1;
len = num2.length();
long long res = -1;
while(left <= right){
long long mid = (left + right) / 2;
n2 = 0;
for(int i = 0; i < len; ++i)
n2 += (isdigit(num2[i]) ? (num2[i] - '0') : (num2[i] - 'a' + 10)) * pow(mid, len - i - 1);
if(n2 == n1){
res = mid;
break;
}
else if(n2 < 0 || n2 > n1) right = mid - 1;
else left = mid + 1;
// else if(n2 < n1) left = mid + 1;
// else right = mid - 1;
}
if(res == -1) cout << "Impossible" << endl;
else cout << res << endl;
return 0;
}