Problem B. Rational Number Tree -Calkin–Wilf tree 数节点编号 基本数据类型范围

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:

  1. 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.
  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, TT 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:

  1. If the problem id is 1, then only one integer n is given, and you are expected to find then-th element of the array.
  2. 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:

  1. If the problem id is 1, then output one line containing "Case #x: p q", where x is the case number (starting from 1), and pq are numerator and denominator of the asked array element, respectively.
  2. If the problem id is 2, then output one line containing "Case #x: n", where x is the case number (starting from 1), and n is the position of the given number.

Limits

1 ≤ T ≤ 100; p and q are relatively prime.

Small dataset

1 ≤ npq ≤ 216-1; p/q is an element in a tree with level number ≤ 16.

Large dataset

1 ≤ npq ≤ 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

推荐指数:※※※

来源:https://code.google.com/codejam/contest/2924486/dashboard#s=p1

这道题首先要分析数的结构。这是一个Calkin–Wilf tree(http://en.wikipedia.org/wiki/Calkin%E2%80%93Wilf_tree)。

树有两个重要的性质。

1.1为根节点编号,按level遍历,某个节点i它的左右孩子节点的编号分别为:2i,2i+1;

2.p/q,当 If a/b < 1, the parent of a/b is a/(b − a); if a/b is greater than one, the parent of a/b is (a − b)/b.

有了这两个性质,就可以递归求解问题。

对于问题1:可以通过性质1判断当前是左节点还是右节点。然后,得知当前p,q是层node节点通过性质2计算得出的。递归,直到当前index为1.

对于问题2,给出了p,q首先通过性质2判断当前是做节点还是右节点。然后通过性质1,计算当前节点的编号。递归。


注意printf的输出数据的格式,采用llu,因为数据高位也可能为1. %lld被坑了。

code:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<vector>
#include<stack>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
typedef struct Node{
unsigned long long     p;
unsigned long long      q;
}Node;
Node find_ele_rec(unsigned long long     index){
	if(index==1){
		Node tmp;
		tmp.p=1;
		tmp.q=1;
		return tmp;
	}
	else{
		Node res=find_ele_rec(index/2);
		if(index&1==1)
			res.p=res.p+res.q;
		else
			res.q=res.p+res.q;
		return res;
	}
}
unsigned long long    find_pos_rec(unsigned long long    p, unsigned long long    q){
	if(p==1&&q==1)
		return 1;
unsigned long long     tmp;
	if(p<q){
		tmp=find_pos_rec(p,q-p);
		return tmp*2;
	}
	else{
		tmp=find_pos_rec(p-q,q);
		return tmp*2+1;
	}
}
int main()
{
	freopen("a.in", "r", stdin);  
   freopen("a.out", "w", stdout); 
	int t;
	int caseid;
	scanf("%d",&t);
	for(caseid=1;caseid<=t;caseid++){
		unsigned long long    qn;
		unsigned long long     p,q,index;
		scanf("%lld",&qn);
		if(qn==1){
			scanf("%llu",&index);
			printf("Case #%d:",caseid);
			Node res_pq=find_ele_rec(index);
			printf(" %llu %llu",res_pq.p,res_pq.q);
		}
		else
		{
			scanf("%llu%llu",&p,&q);
			 unsigned long long     res= find_pos_rec( p,q);
		    printf("Case #%d:",caseid);
			printf(" %llu",res);
		}
		printf("\n");
	}
	return 0;
}


对于基本数据类型范围,贴一个 http://blog.csdn.net/wuxiaoyao12/article/details/7532244比较详细的讲解:

char                           -128 ~ +127                (1 Byte)

short                        -32767 ~ + 32768     (2 Bytes)
unsigned short      0 ~ 65536                   (2 Bytes)

int                -2147483648 ~ +2147483647    (4 Bytes)
unsigned int          0 ~ 4294967295     (4 Bytes)

double                     1.7 * 10^308             (8 Bytes)
long (==int)-2147483648 ~ +2147483647    (4 Bytes)
long long         -9223372036854775808 ~ +9223372036854775807    (8 Bytes)

 

Dev-C++下基本类型所占位数和取值范围:

符号属性      长度属性      基本型      所占位数      取值范围    输入符举例       输出符举例

--                       --                char          8            -2^7 ~ 2^7-1        %c             %c、%d、%u
signed             --                char          8            -2^7 ~ 2^7-1        %c             %c、%d、%u
unsigned        --                char          8              0 ~ 2^8-1           %c             %c、%d、%u

[signed]        short             [int]          16         -2^15 ~ 2^15-1                                %hd
unsigned      short             [int]          16             0 ~ 2^16-1                         %hu、%ho、%hx

[signed]          --                 int            32        -2^31 ~ 2^31-1                                  %d
unsigned          --               [int]          32             0 ~ 2^32-1                            %u、%o、%x

[signed]          long             [int]          32        -2^31 ~ 2^31-1                                  %ld
unsigned        long             [int]          32             0 ~ 2^32-1                          %lu、%lo、%lx

[signed]         long long      [int]          64         -2^63 ~ 2^63-1                                %I64d
unsigned       long long      [int]          64             0 ~ 2^64-1                      %I64u、%I64o、%I64x

--                       --             float          32        +/- 3.40282e+038                     %f、%e、%g
--                       --           double        64        +/- 1.79769e+308            %lf、%le、%lg    %f、%e、%g
--                     long         double        96        +/- 1.79769e+308                   %Lf、%Le、%Lg

 

几点说明:

1. 注意表中的每一行,代表一种基本类型。“[ ]”代表可省略。
    例如:char、signed char、unsigned char是三种互不相同的类型;
    int、short、long也是三种互不相同的类型。
    可以使用C++的函数重载特性进行验证,如:
    void Func(char ch) {}
    void Func(signed char ch) {}
    void Func(unsigned char ch) {}
    是三个不同的函数。

2. char/signed char/unsigned char型数据长度为1字节;
    char为有符号型,但与signed char是不同的类型。
    注意,并不是所有编译器都这样处理,char型数据长度不一定为1字节,char也不一定为有符号型。

3. 将char/signed char转换为int时,会对最高符号位1进行扩展,从而造成运算问题。
    所以,如果要处理的数据中存在字节值大于127的情况,使用unsigned char较为妥当。//因为此时范围与整型相对统一起来了
    程序中若涉及位运算,也应该使用unsigned型变量。

4. char/signed char/unsigned char输出时,使用格式符%c(按字符方式);或使用%d、%u、%x/%X、%o,按整数方式输出;
    输入时,应使用%c,若使用整数方式,Dev-C++会给出警告,不建议这样使用。

5. int的长度,是16位还是32位,与编译器字长有关。
    16位编译器(如TC使用的编译器)下,int为16位;32位编译器(如VC使用的编译器cl.exe)下,int为32位。

6. 整型数据可以使用%d(有符号10进制)、%o(无符号8进制)或%x/%X(无符号16进制)方式输入输出。
    而格式符%u,表示unsigned,即无符号10进制方式。

7. 整型前缀h表示short,l表示long。
    输入输出short/unsigned short时,不建议直接使用int的格式符%d/%u等,要加前缀h。
    这个习惯性错误,来源于TC。TC下,int的长度和默认符号属性,都与short一致,
    于是就把这两种类型当成是相同的,都用int方式进行输入输出。

8. 关于long long类型的输入输出:
    "%lld"和"%llu"是linux下gcc/g++用于long long int类型(64 bits)输入输出的格式符。
    而"%I64d"和"%I64u"则是Microsoft VC++库里用于输入输出__int64类型的格式说明。
    Dev-C++使用的编译器是Mingw32,Mingw32是x86-win32 gcc子项目之一,编译器核心还是linux下的gcc。
    进行函数参数类型检查的是在编译阶段,gcc编译器对格式字符串进行检查,显然它不认得"%I64d",
    所以将给出警告“unknown conversion type character `I' in format”。对于"%lld"和"%llu",gcc理所当然地接受了。
    Mingw32在编译期间使用gcc的规则检查语法,在连接和运行时使用的却是Microsoft库。
    这个库里的printf和scanf函数当然不认识linux gcc下"%lld"和"%llu",但对"%I64d"和"%I64u",它则是乐意接受,并能正常工作的。

9. 浮点型数据输入时可使用%f、%e/%E或%g/%G,scanf会根据输入数据形式,自动处理。
    输出时可使用%f(普通方式)、%e/%E(指数方式)或%g/%G(自动选择)。

10. 浮点参数压栈的规则:float(4 字节)类型扩展成double(8 字节)入栈。
     所以在输入时,需要区分float(%f)与double(%lf),而在输出时,用%f即可
     printf函数将按照double型的规则对压入堆栈的float(已扩展成double)和double型数据进行输出。
     如果在输出时指定%lf格式符,gcc/mingw32编译器将给出一个警告。

11. Dev-C++(gcc/mingw32)可以选择float的长度,是否与double一致。

12. 前缀L表示long(double)。
     虽然long double比double长4个字节,但是表示的数值范围却是一样的。
     long double类型的长度、精度及表示范围与所使用的编译器、操作系统等有关。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值