Problem
Consider an infinite complete binary tree where the root node is 1/1 and left and right childs of node p/q are p/(p+q) and (p+q)/q, respectively. This tree looks like:
1/1 ______|______ | | 1/2 2/1 ___|___ ___|___ | | | | 1/3 3/2 2/3 3/1 ...It is known that every positive rational number appears exactly once in this tree. A level-order traversal of the tree results in the following array:
1/1, 1/2, 2/1, 1/3, 3/2, 2/3, 3/1, ...
Please solve the following two questions:
- Find the n-th element of the array, where n starts from 1. For example, for the input 2, the correct output is 1/2.
- Given p/q, find its position in the array. As an example, the input 1/2 results in the output 2.
Input
The first line of the input gives the number of test cases, T. T test cases follow. Each test case consists of one line. The line contains a problem id (1 or 2) and one or two additional integers:
- If the problem id is 1, then only one integer n is given, and you are expected to find the n-th element of the array.
- If the problem id is 2, then two integers p and q are given, and you are expected to find the position of p/q in the array.
Output
For each test case:
- If the problem id is 1, then output one line containing "
Case #x: p q
", wherex
is the case number (starting from 1), andp
,q
are numerator and denominator of the asked array element, respectively. - If the problem id is 2, then output one line containing "
Case #x: n
", wherex
is the case number (starting from 1), andn
is the position of the given number.
Limits
1 ≤ T ≤ 100; p and q are relatively prime.
Small dataset
1 ≤ n, p, q ≤ 216-1; p/q is an element in a tree with level number ≤ 16.
Large dataset
1 ≤ n, p, q ≤ 264-1; p/q is an element in a tree with level number ≤ 64.
Sample
Input | Output |
4 1 2 2 1 2 1 5 2 3 2 | Case #1: 1 2 Case #2: 2 Case #3: 3 2 Case #4: 5 |
类型:数论 图论 难度:2
题意:有一棵二叉树,每个节点为p/q,根节点p=q=1,对每个根节点,左子结点为p/(p+q),右子结点为(p+q)/q,每个节点有一个编号,为层序遍历的顺序编号,从1开始,问:给出编号n求该点的pq,给出pq求该点的编号n
分析:基础二叉树和数论结合的题,做的时候绕了个圈子,实际上一看到二叉树和给出编号求值得问题,就该想到利用编号的二进制编码来求路径,类似Hoffman编码的方法。知道了根节点到编号n节点的遍历路径,自然根据pq的算法求得n对应的pq。而根据pq求n就是反推的结果,因为p<q,则当前节点是其父亲的左儿子,p>q则是右儿子,反推出n即可。具体方法如下:
关键:将编号n看成二进制序列,初始化为1,从根节点向下遍历,向左子树走向末位添0,向右子树走向末位添1,最后结果即为n。例如,第n=5=(101)2,即第一个1表示根节点,第二个0表示向左走,第三个1表示向右走,即达到位置5。
1、根据n求pq:根据上述方法从根节点走到n,向左走时q+=p,向右走时p+=q,可求得节点n的pq
2、根据pq求n:最高位初始化,第1位,p<q,为左儿子,最高位置为0,q-=p;p>q,为右儿子,最高位置为1,p-=q。每次循环最高位+1。最后再补一个最高位1(根节点的1),即求得n
代码:
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<iostream>
#define MIN(x,y) (x)<(y)?(x):(y)
using namespace std;
const int N=1010;
void fun(unsigned long long n,unsigned long long &p,unsigned long long &q)
{
p = q = 1;
int num[100];
memset(num,0,sizeof(num));
int ct = 0;
while(n)
{
num[ct++] = n&1;
n >>= 1;
}
for(; num[ct]==0; ct--) ;
ct--;
for( ; ct>=0; ct--)
{
if(num[ct])
{
p += q;
}
else
{
q += p;
}
}
}
void fun2(unsigned long long &n,unsigned long long p,unsigned long long q)
{
n = 0;
unsigned long long tmp = 1;
while(p!=1 || q!=1)
{
if(p<q)
{
q -= p;
}
else
{
n |= tmp;
p -= q;
}
tmp <<= 1;
}
n |= tmp;
}
int main()
{
freopen("B-large.in","r",stdin);
freopen("B-large.out","w",stdout);
int t;
scanf("%d",&t);
for(int cnt=1;cnt<=t;cnt++)
{
int type;
scanf("%d",&type);
unsigned long long n,p,q;
if(type == 1)
{
scanf("%llu",&n);
fun(n,p,q);
printf("Case #%d: %llu %llu\n",cnt,p,q);
}
else
{
scanf("%llu%llu",&p,&q);
fun2(n,p,q);
printf("Case #%d: %llu\n",cnt,n);
}
}
}