HDU2446——Shell Pyramid 详解
- Shell Pyramid
-
题目描述(Google 翻译的)
在17世纪,由于雷鸣般的喧嚣,浓烟和炽热的火焰,海上的战斗与现代战争一样。但那时,大炮非常简单。它就像一个铁缸,其后端密封,前端打开。它的后端有一个小孔,用来安装保险丝。战舰上的大炮被放在有四个轮子的小型车辆上,炮弹是铁球,里面装着火药。当时,据说有一位聪明的船长,他也是一位数学家的业余爱好者。他喜欢把他遇到的所有东西都连接到数学上。在每次战斗之前,他经常命令士兵将炮弹放在甲板上并使这些炮弹形成炮弹金字塔。
现在让我们假设一个壳金字塔有四层,每层都会有一系列序数。它们如下图所示:
在该图中,它们分别是从左到右的第一层,第二层,第三层和第四层。
在第一层中,只有1个壳,其序数为1.在第二层中,有3个壳,它们的序数为1,2和3.在第三层中,有6个壳,它们的序数分别为1,2,3,4,5和6.在第四层中,有10个壳,它们的序数在上图中显示。
整个贝壳金字塔也有序列号。例如,第二层中第三个外壳的序列号为4,第三层中第五个外壳的序列号为9,第四层中第九个外壳的序列号为19。
还有一个相互关联的问题:如果给出一个序列号s,那么我们可以计算出第s个shell是在什么层,什么行和什么列。假设层数是i,行数是j而列数是k,因此,如果s = 19,则i = 4,j = 4并且k = 3。
现在让我们继续讲述船长的故事。
一场战斗即将开始。船长给每个大炮分配了相同数量的炮弹。炮弹堆放在甲板上,由炮弹形成相同的炮弹金字塔。当敌人的战舰靠近时,船长命令同时开火。然后听到了雷鸣般的声音。船长仔细听了,然后他就知道有多少炮弹被使用了,剩下多少炮弹了。在战斗结束时,船长赢了。在休息期间,他向下属询问了一个问题:对于壳金字塔,如果给出序列号s,你如何计算层数i,行号j和列号k?
- 输入
首先输入一个数字n,再进行n个案例。对于每种情况,都有一个足够大的壳金字塔,给出一个整数,这个整数是序列号s(s <2 ^ 63)。有几个测试用例。输入由文件末尾终止。 - 输出
对于每种情况,输出相应的层编号i,行编号j和列编号k。 - 样例输入
2
19
75822050528572544 - 样例输出
4 4 3
769099 111570 11179 - 思路分析
- 首先通过二分先确定给出的数字是哪个金字塔,要找到是哪个金字塔,就需要知道给定序号代表的意思,序号是宏观的炮弹下标,因此它必然代表之前已经出现的所有炮弹,要找到它属于第几个金字塔,就需要一个数组来存储对于金字塔序号 i,它和它之前所有的金字塔会产生多少炮弹。
- 再通过二分确定是属于这个金字塔的哪一层。要找到它是哪一层,就需要知道这个金字塔有多少个炮弹,因此还需要一个数组来存储对于金字塔序号 i,它自己会产生多少炮弹。
- 设计思路
- 对于第 i 个金字塔,它所包含的炮弹数都是第 i-1 个金字塔所包含的炮弹数加上 i,比如第二个金字塔有 3 个炮弹,那么第三个就有 3+3 个炮弹,因此得到非递减序列 a[i] = a[i-1] + i
- 对于第 i 个金字塔,所有的炮弹数目为 i-1 个金字塔所包含炮弹数目与自己含有炮弹之和,因此得到非递减序列 sum[i] = sum[i-1] + a[i]
- 接下来只需要两次二分找出结果,找到金字塔序号 p 后,减去sum[p-1],就会得到所在金字塔中第多少个炮弹,找到行号 row 后,减去a[row-1],就是列号。
#include <iostream> #include <cstdio> #include<cmath> #define Maxn 1000000 using namespace std; typedef long long int LL; LL a[Maxn]; LL sum[Maxn]; LL find_index(LL *array,LL size,LL key); int main() { int i; a[1] = 1; for(i=2; i<Maxn; i++) { a[i] = a[i-1]+i; } sum[1] = 1; for(i=2; i<Maxn; i++) { sum[i] = sum[i-1]+a[i]; } int t; scanf("%d",&t); while(t--) { LL num; cin >> num; LL p = find_index(sum,Maxn,num); num -= sum[p-1]; LL row = find_index(a,Maxn,num); LL col = num-a[row-1]; cout<<p<<" "<<row<<" "<<col<<endl; } return 0; } LL find_index(LL *array,LL size,LL key) { int first = 0, last = size-1; int middle, pos=0; while(first < last) { middle = (first+last)/2; if(array[middle] < key) { first = middle + 1; pos = first; } else { last = middle; pos = last; } } return pos; }
- 对于非递减序列,可以使用 STL 中的 lower_bound()
#include <iostream> #include <cstdio> #include<cmath> #define Maxn 1000000 using namespace std; typedef long long int LL; LL a[Maxn]; LL sum[Maxn]; int main() { int i; a[1] = 1; for(i=2;i<Maxn;i++) { a[i] = a[i-1]+i; } sum[1] = 1; for(i=2;i<Maxn;i++) { sum[i] = sum[i-1]+a[i]; } int t; scanf("%d",&t); while(t--) { LL num; cin >> num; LL p = lower_bound(sum,sum+Maxn,num)-sum; num -= sum[p-1]; LL row = lower_bound(a,a+Maxn,num)-a; LL col = num-a[row-1]; cout<<p<<" "<<row<<" "<<col<<endl; } return 0; }