题目链接
本题有几个注意点:
1.对基数进行遍历会超时,因此要用二分法
2.本题变量最好使用long long型,因为待求radix可以很大,在转换为十进制过程中甚至会超出long long 的表示范围,不过本题的给出的数转换为十进制不会超过范围,因此只需检查未知进制的数是否超出范围,在超出long long表示范围时,会变成负数,可将此作为判断依据。
3.待求进制的下界是给出数中的最大位+1(这是由进制表示规定的),考虑当需求数的radix= 已确立数时,其表示为10,所以当radix= 已确立数+ 1时,其恰好能用1位数来表示,如果radix继续增大,还是能用1位数表示,但是用1位数表示多出来的部分已经不必考虑了,因为会比需求数大。上界是给出数转化为十进制数的结果与下界取最大值再加1。
4.可以用一个二维数组n[2][15]来表示给出数和待求数,可以验证,无论tag取1或2,n[tag-1]始终指向给出数,n[2-tag]始终指向待求数,这样可以方便很多。
#include<cstdio>
#include<algorithm>
using namespace std;
int Map[256];//0~9、a~z与0~35对应
void init(){
for(char c='0';c<='9';c++){//将字符转换为对应的整数
Map[c]=c-'0';
}
for(char c='a';c<='z';c++){
Map[c]=c-'a'+10;
}
}
long long convertnum10(char a[],long long radix){//转换为十进制
long long ans=0;
for(int i=0;a[i]!='\0';i++){
ans*=radix;
ans+=Map[a[i]];
if(ans<0) return -1;//溢出long long的表示范围,要及时返回
}
return ans;
}
int findLargestDigit(char a[]){//找待求数组中的最大数,加一作为二分下界
int ans=-1;
for(int i=0;a[i]!='\0';i++){
if(Map[a[i]]>ans) ans=Map[a[i]];
}
return ans+1;
}
int main(){
init();
char n[2][15];//n[tag-1]总为给出基数的数组,n[2-tag]总为待求数组
int tag,radix;
long long num1=0,num2=0;
//num1,num2分别为给出数组和待求数组在对应的基数之下转化为十进制的数
scanf("%s%s%d%d",n[0],n[1],&tag,&radix);
num1=convertnum10(n[tag-1],radix);
long long left=findLargestDigit(n[2-tag]);
long long right=max(left,num1)+1,mid;//上界最多可以是num1+1或left+1
while(left<=right){//二分
mid=(left+right)/2;
num2=convertnum10(n[2-tag],mid);
//用二分出来的基数将待求数组转化为十进制数
if(num2==num1) {
printf("%lld",mid);
return 0;
}
else {
if(num2>num1||num2<0)//转换后的结果大于给出数或者转化时溢出
right=mid-1;
else left=mid+1;//转换后的结果小于给出数
}
}
printf("Impossible");//没有找到满足条件的基数
return 0;
}