2021年度训练联盟热身训练赛第二场 J Lowest Common Ancestor 进制转换

16 篇文章 1 订阅

【2021年度训练联盟热身训练赛第二场】
J Lowest Common Ancestor (进制转换)

【题目描述】

Perfect binary trees are one of the coolest structures that computer scientists study. They have a lot of properties that make them very nice to work with. One of the nicest properties is that they can just be described by a single integerngiving the depth of the tree. For instance, the perfect binary tree forn= 3 looks like:
在这里插入图片描述
In general, a perfect binary tree with depthnwill have exactly 2n+1 – 1 nodes, and can be numbered by following the pattern seen above (there are of course other ways to number the nodes of the tree, but this is the scheme we will use in this problem).

A common question that arises when dealing with trees is the query of the lowest common ancestor (commonly called LCA) of two nodes in the tree. Formally, the LCA ofxandyis the nodezof greatest depth in the tree such thatzis an ancestor ofxandy. Nodeais an ancestor of nodecifcexists in the sub-tree rooted at nodea. Notice that 1 is trivially a common ancestor of any two nodes in the tree, but is not always thelowestcommon ancestor. For instance, the common ancestors of nodes 7 and 12 are 1 and 3, and 3 is the LCA since it is the node of greatest depth. The LCA of 2 and 13 is node 1, and the LCA of 5 and 11 is node 5. The definition of LCA guarantees that the LCA of any two nodes will always be unique.

The Problem:
Given two nodes in the tree using the numbering scheme shown above, determine the LCA of the two nodes.

【输入描述】

Input will begin with apositive integer,T≤ 2∙10^6,indicating the number of test cases. Thiswill be followed byTtest cases, each on a separate inputline. Each test case will contain twospace separated integers,XandY, represented in hexadecimal.XandYwill each contain at most 1000 characters from the set {0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}, where a-frepresent 10-15, respectively. You are todetermine the LCA ofXandY.

Note: The hexadecimal (base 16) numberdndn-1···d1d0 is converted to a decimal number (base 10) by the followingformula:d0·160 +d1·161 + ··· +dn-1·16n-1 +dn·16n.

【输出描述】

For each case, output a singleline:

Case#x:y

wherexis the case number beginning with 1, andyis the LCA in hexadecimal with no leading 0’s. Leave a blankline after the output for each testcase.

【样例输入】

7
7 c
2 d
b 5
10 11
a020fac a030ccf
12afcdb 12afcdc
100000000 fffffffff

【样例输出】

Case #1: 3

Case #2: 1

Case #3: 5

Case #4: 8

Case #5: 501

Case #6: 255f9b

Case #7: 1

【解题思路】

这道题应该通过进制转换来完成,将十六进制的数转为二进制数,找到两个数的最近公共祖先(即二进制下两个数的最长公共前缀),然后再将其转回十六进制即可。

比赛的时候因为没有看清十六进制数可以最多包含1000个字符,导致一直TLE也没有发现问题在哪,这样显然就不能通过转换成整数类型的十进制数来做了,必须采用字符串储存数字,要么就用Python来做。

AC代码

使用Python可以很方便地进行进制转换,而且Python中的int类型没有长度限制:

T=int(input())
for t in range(T):
    str1,str2=input().split(" ")
    a=int(str1,16)
    b=int(str2,16)
    while a!=b :
        if a>b : a=a//2
        else: b=b//2
    #print("Case #%d: %s\n"%(t+1,hex(a)[2:]))
    print("Case #{}: {}\n".format(t+1,hex(a)[2:]))

C++代码:(转二进制)

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <map>
using namespace std;
/*
char num[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
long long h_to_d(string s)//十六进制转十进制 
{
	int len=s.length();
	long long ans=0,temp=1;
	for(int i=0;i<len;i++)
	{
		long long t;
		if(s[len-1-i]>='0'&&s[len-1-i]<='9')
			t=s[len-1-i]-'0';
		else
			t=s[len-1-i]-'a'+10;
		ans+=t*temp;
		temp*=16;
	}
	return ans;
}
string d_to_h(long long n)//十进制转十六进制 
{
	stack<char> s;
	string ans;
	while(n!=0)
	{
		s.push(num[n%16]);
		n/=16;
	}
	if(s.empty())
		ans="0";
	while(!s.empty())
	{
		ans+=s.top();
		s.pop();
	}
	return ans;
}
*/
string bin[16]={"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"};
map<string,string> mp;
int main()
{
	mp["0000"]="0";mp["0001"]="1";mp["0010"]="2";mp["0011"]="3";mp["0100"]="4";mp["0101"]="5";mp["0110"]="6";mp["0111"]="7";
	mp["1000"]="8";mp["1001"]="9";mp["1010"]="a";mp["1011"]="b";mp["1100"]="c";mp["1101"]="d";mp["1110"]="e";mp["1111"]="f";
	int T;
	cin>>T;
	for(int t=1;t<=T;t++)
	{
		string a,b,c,a2,b2,c2;
		cin>>a>>b;
		int len1=a.length();
		for(int i=0;i<len1;i++)
		{
			int t;
			if(a[i]>='0'&&a[i]<='9')
				t=a[i]-'0';
			else
				t=a[i]-'a'+10;
			a2+=bin[t];
		}
		int len2=b.length();
		for(int i=0;i<len2;i++)
		{
			int t;
			if(b[i]>='0'&&b[i]<='9')
				t=b[i]-'0';
			else
				t=b[i]-'a'+10;
			b2+=bin[t];
		}//先转换为二进制 
		while(a2[0]=='0')
			a2.erase(0,1);
		while(b2[0]=='0')
			b2.erase(0,1);//去前导零 
		//cout<<a2<<endl<<b2<<endl;
		int lena=a2.length(),lenb=b2.length();
		int lenm=min(lena,lenb);
		for(int i=0;i<lenm;i++)
		{
			if(a2[i]==b2[i])
				c2+=a2[i];
			else
				break;
		}//求二进制下的最近公共祖先 
		while(c2.length()%4)
			c2="0"+c2;//补前导零 
		while(c2.length()>0)
		{
			c+=mp[c2.substr(0,4)];
			c2.erase(0,4);
		}//二进制转十六进制 
		printf("Case #%d: %s",t,c.c_str());
		printf("\n\n");
	}
	return 0;
}

比赛时范围不够的TLE代码:

#include <iostream>
#include <cstdio>
#include <string>
using namespace std;
char num[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
long long h_to_d(string s)//十六进制转十进制 
{
	int len=s.length();
	long long ans=0,temp=1;
	for(int i=0;i<len;i++)
	{
		long long t;
		if(s[len-1-i]>='0'&&s[len-1-i]<='9')
			t=s[len-1-i]-'0';
		else
			t=s[len-1-i]-'a'+10;
		ans+=t*temp;
		temp*=16;
	}
	return ans;
}
/*
string d_to_h(long long n)//十进制转十六进制 
{
	stack<char> s;
	string ans;
	while(n!=0)
	{
		s.push(num[n%16]);
		n/=16;
	}
	if(s.empty())
		ans="0";
	while(!s.empty())
	{
		ans+=s.top();
		s.pop();
	}
	return ans;
}
*/
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T;
	cin>>T;
	for(int t=1;t<=T;t++)
	{
		string a,b;
		cin>>a>>b;
		long long aa,bb,cc;
		aa=h_to_d(a);
		bb=h_to_d(b);
		while(1)
		{
			if(aa>bb) aa>>=1;
			if(bb>aa) bb>>=1;
			if(aa==bb)
			{
				cc=aa;
				break;
			}
		}
		//c=d_to_h(cc);
		printf("Case #%d: ",t);
		char c[20];
		int pos=0;
		while(cc!=0)
		{
			c[pos++]=num[cc%16];
			cc/=16;
		}
		for(int i=pos-1;i>=0;i--)
			printf("%c",c[i]);
		printf("\n\n");
	}
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

球王武磊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值