1010 Radix(绝妙二分题)

据说点这里能进入地狱

FB70319368DD30C13F9483A285F0CD01

解题思路

这是一道较难的题目,题解是二分。二分的题目我是真看不出来。

题目给出两个数 N1 , N2 , 再给出一个 tag 和 radix , 给出的是第tag个数的进制。题目问两个数是否相等,实际上我们可以将两者转换为十进制,看他们是否相等,比如 30 110 1 4 ,30在这里是4进制数,先转换为10进制,就是3 × 4 + 0 × 1 = 12, 然后找到一个进制使得110在这个进制下转换为十进制的数值等于12。

可以将这个求解问题转化为判定问题,即我猜一个数,这个数在所有可能的解的区间内,然后看这个数是否满足我的要求,由于该问题具有单调性,即一个固定的数,其进制越大,转化的数值也越大,不断二分确定解的区间。

这里要注意一点 由于输入示例中存在字符,所以要以字符串的形式输入,然后把已知进制的数的十进制数求出来,作为目标值,然后进行二分。

  • 同时,如果数中有字符,每个字符是独立的一位。

ac✅️代码


#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;

ll GetCharValue(char c)
{
	if(isdigit(c))
	{
		return c - '0';
		
	}
	return c - 'a' + 10;//以a为起点,数值是10,所以要加10
}

//猜一个进制,将N2转化为十进制
ll CalculateValue(string s,ll radix )
{
	ll res = 0;
	for(char c : s)
	{
		ll val = GetCharValue(c);
        //相乘的过程可能会超过 ll 的范围,所以要用(double),一旦乘法溢出,可能会变成负数.
        //由于本题给出的进制最大是36,而N1的位数最多是十位,所以target值最大能取到36^10
        //36略大于2^5, 就是2的50次方,又因为2的10次方约等于10的三次方,所以大概在10的15次方 左右,所以选取一个合理的2e18
		if((double)res * radix + val > 2e18 )
		{
			return -1;
		}
		res = res * radix + val;
	}
	return res;
}

int main()
{
	string n1,n2;
	ll tag,radix;
	
	if(!(cin>>n1>>n2>>tag>>radix))
	{
		return 0;
	}
	
    //统一处理,让N2成为未确定进制的数
	if(tag == 2) swap(n1,n2);
	
    //确定target值
	ll target = 0;
	for(char c : n1)
	{
		target = target * radix + GetCharValue(c);
	}
	
	//要确定N2的进制边界,按它给出的数确定它的最小可能进制,比如五进制的数里面不可能有6。11进制的数不可能有c
	ll low = 0;
	for(char c : n2)
	{
		low = max(low,GetCharValue(c));
	}
	//加一才是最小边界,因为进制大于所有位上的数
	low ++;
	
	//这一步特别妙,实在是高人,high范围确定的太准了
	ll high = max(low , target) + 1;

    /*
      在极端情况下,比如target值达到最大,而N2是10,其转化为十进制为1×进制 ,
      这样其要达到target,进制也要很大,所以取target+1让它足够大

      这样右边界我们就确定了,然后确定左边界,一个数其最小边界就是我们上面说的low,
      由于这里求的是high,low已经定了,high可以大一些,所以要加一,当然这里不加一也能ac。
       这里加一其实有点说法的,二分区间长度一般来说要大于等于2,嗯,差不多这样就行了。
    */
  
	ll ans = -1;
		
	while(low <= high)
	{
		ll mid = low + (high - low) / 2;
		ll val = CalculateValue(n2 , mid);
		
		if(val == target)
		{
			ans = mid ;
			//这里找到答案了,但是不能break,因为题目要求如果存在多解就输出最小值,所以还要在左边去检查一遍
			high = mid - 1;
		}
		else if(val == -1 || val > target)
		{
			high = mid - 1;
		}
		else low = mid + 1;
		
	}
	
	if(ans == -1) cout<<"Impossible"<<endl;
	else cout<<ans<<endl;
	
	return 0;
	
}
posted @ 2026-03-31 17:02  shuiwangrenjia  阅读(7)  评论(0)    收藏  举报