【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;
}