题目链接:点击打开链接
题目大意:给定两个数,其中单个位置上的数值范围可以为 [0-z]。指定其中一个数的进制,试确定是否存在可能的进制让两数的实际值相等。(如果有多个答案,要求最小进制值)
解题思路:
1、input 中两个数字可以是 10 位数,虽然没有告诉 radix 的范围,但在9*10^10 10 1 200这个示例中,可以看到结果的 radix 也可以是很大的。从这个角度看,代码中将 radix 和两个数值都设定为 longlong 是合适的选择。
2、在计算另一个数的 radix 时,简单的遍历 [2, 1018]会超时。单调的区间很自然想到使用二分查找。
3、二分查找的上下界确定能减少耗时:下界选数字的所有位上的最大值+1;上界容易想当然的认为就是题中给定了 radix 的数的值。实际上,示例11 b 1 10就是一个反例,原因在于这个假设忽略了一位数的可能性,解决方案是在取给定 radix 的数值和下界中较大的那个数。
4、二分查找时,不可直接计算出某个 radix 下数的值,因为可能会 longlong 溢出。于是需要用特定的 cmp 函数,在累加的过程中判定是否大于另一个数。算是一种剪枝。
5、当两个数都是 1 时,输出 2。
AC 代码
#include<bits/stdc++.h>
#include<cmath>
#define mem(a,b) memset(a,b,sizeof a);
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
string a[3];
ll tag,radix,num;
int cal(char c)
{
if(c>='0' && c<='9') return c-'0';
else return c-'a'+10;
}
ll Any2D(string s, ll rdx)
{
ll ans=0, tmp=1;
for(int i=s.length()-1;i>=0;i--)
{
ans+=cal(s[i])*tmp;
tmp*=rdx;
}
return ans;
}
int cmp(string s,ll rdx)
{
ll ans=0, tmp=1;
for(int i=s.length()-1;i>=0;i--)
{
ans+=cal(s[i])*tmp;
if(ans>num) return 1;
tmp*=rdx;
}
return ans==num?0:-1;
}
int maxR(string s)
{
int ans=-1;
for(int i=0;i<s.length();i++)
ans=max(ans,cal(s[i]));
return ans+1;
}
ll bs(int cur) // 此二分类型是:求最小的i,使得a[i] = key
{
ll l=maxR(a[cur]), r=max(l,num), m, rs;
while(l<r)
{
m=l+((r-l)>>1);
rs=cmp(a[cur],m);
if(rs==-1) l=m+1;
else r=m;
}
return cmp(a[cur],r)==0?r:0;
}
int main()
{
while(cin>>a[1]>>a[2]>>tag>>radix)
{
if(a[1]=="1" && a[2]=="1") puts("2");
// else if(a[1]==a[2]) printf("%lld\n",radix);
else
{
num=Any2D(a[tag],radix);
int cur=tag==1?2:1, ans=bs(cur);
if(ans) printf("%lld\n",ans);
else puts("Impossible");
}
}
return 0;
}