A - Dropping Balls
A number of K balls are dropped one by one from the root of a fully binary tree structure FBT. Each
time the ball being dropped first visits a non-terminal node. It then keeps moving down, either follows
the path of the left subtree, or follows the path of the right subtree, until it stops at one of the leaf
nodes of FBT. To determine a ball’s moving direction a flag is set up in every non-terminal node with
two values, either false or true. Initially, all of the flags are false. When visiting a non-terminal node
if the flag’s current value at this node is false, then the ball will first switch this flag’s value, i.e., from
the false to the true, and then follow the left subtree of this node to keep moving down. Otherwise,
it will also switch this flag’s value, i.e., from the true to the false, but will follow the right subtree of
this node to keep moving down. Furthermore, all nodes of FBT are sequentially numbered, starting at
1 with nodes on depth 1, and then those on depth 2, and so on. Nodes on any depth are numbered
from left to right.
For example, Fig. 1 represents a fully binary tree of maximum depth 4 with the node numbers 1,
2, 3, …, 15. Since all of the flags are initially set to be false, the first ball being dropped will switch
flag’s values at node 1, node 2, and node 4 before it finally stops at position 8. The second ball being
dropped will switch flag’s values at node 1, node 3, and node 6, and stop at position 12. Obviously,
the third ball being dropped will switch flag’s values at node 1, node 2, and node 5 before it stops at
position 10.
Fig. 1: An example of FBT with the maximum depth 4 and sequential node numbers.
Now consider a number of test cases where two values will be given for each test. The first value is
D, the maximum depth of FBT, and the second one is I, the I-th ball being dropped. You may assume
the value of I will not exceed the total number of leaf nodes for the given FBT.
Please write a program to determine the stop position P for each test case.
For each test cases the range of two parameters D and I is as below:
2 ≤ D ≤ 20, and 1 ≤ I ≤ 524288.
Input
Contains l + 2 lines.
Line 1 l the number of test cases
Line 2 D1 I1 test case #1, two decimal numbers that are separated by one blank
…
Line k + 1 Dk Ik test case #k
Line l + 1 Dl Il test case #l
Line l + 2 -1 a constant ‘-1’ representing the end of the input file
Output
Contains l lines.
Line 1 the stop position P for the test case #1
…
Line k the stop position P for the test case #k
…
Line l the stop position P for the test case #l
Sample Input
5
4 2
3 4
10 1
2 2
8 128
-1
Sample Output
12
7
512
3
255
分析:
本題,首先題意我是看不懂的。不得不借助一下翻譯。
在瞭解了本題的題意後,我發現可以根據滿二叉樹的性質來寫這道題。其實也沒必要去模擬每個小球的下墜過程。我們如果瞭解了二叉樹的性質之後,我們會發現,在這裡我們可以不用建樹。而是將每個結點儲存在一個數組裡面(滿二叉樹的性質)。在這個一維數組裡面,下標為0的結點是根結點。下標為2i的結點是下標為i的結點的左結點。下標為2i+1的結點是下標為i的結點的右結點。那麼整個小球的下墜過程,就可以用下標的不斷移動來表示。然後,整個一維數組裡的每一個數組元素,表示一個結點。我們可以定義一個結構體,結構體裡面有值,還有往哪邊走的標誌值。然後,在定義一個結構體的一維陣列。並且,注意在每一個球的下落後,標誌值要改變。
如下程式碼:
#include"stdio.h" #include"string.h" typedef struct BiTNode{ long long node; long long left;//在此題中完全可以不用定義左右孩子的結點值下標。 long long right; int mark; }BiTNode; BiTNode NODE[10000000]; int main(){ long long i,D,j,k,T; while(~scanf("%lld",&T)){ if(T==-1) break; while(T--){ scanf("%lld",&D); for(i=1;i<=pow(2,D)-1;i++){ NODE[i].node=i; NODE[i].left=2*i; NODE[i].right=2*i+1; NODE[i].mark=0; } scanf("%lld",&i); for(j=1;j<=i;j++){ for(k=1;k<=pow(2,D-1)-1;){ if(NODE[k].mark==0){ NODE[k].mark=1; k=NODE[k].left; } else{ NODE[k].mark=0; k=NODE[k].right; } } } printf("%lld\n",k); } } }
不過當我提交的時候,會發現時間超限。證明此思路可行。但無法通過大量資料。然後,經過思考,我們會發現。對於如果有N(N為偶數)個球。那麼首先,可以肯定的是,最後的下落點在根的右側。同樣我們繼續考慮。最後的下落點在根的右側。那麼到達根的右孩子的球的個數,只能有N/2個。然後我們再考慮N/2的奇偶。如果為奇,則最後的下落點在根的右孩子的左側,且有(N/2+1)/2個球落在左側。否則相反。
如此,一直向下考慮。知道N為1,為止。
如此一來此題,變了一個思維題。
不過在此思路中,我們可以用一個數組來儲存一個樹的葉結點。不需要考慮雙親結點。
程式碼如下:
#include"stdio.h"
#include"string.h"
long long a[10000000];//當D為20的時候,葉結點巨多。
int main()
{
long long D,I,N;
long long i,j,start,end,mid,length;
while(~scanf("%lld",&N))
{ if(N==-1)
break;
while(N--)
{
scanf("%lld%lld",&D,&I);
end=start=1;
for(i=2;i<=D;i++)//利用此迴圈,可以算出葉結點最左邊的值和最右邊的值
{start=2*start;
end=2*end+1;
}
/* printf("%lld %lld\n",start,end);*/
length=end-start+1;//葉結點的個數, 也用作陣列下標
for(i=1;i<=length;i++)//將葉結點的值儲存
a[i]=start++;
/*// for(i=1;i<=length;i++)
// printf("%lld ",a[i]);*/
start=1;//下標
end=length;
while(I>1)//這裡是核心程式碼
{
if(I%2==0)//如果有偶數個球的處理方式。
{
mid=length/2+start-1;/*這裡之所以/2之後還要把最左邊的下標值加上,已保證,下標值能正確指向中間的那位數。*/
start=mid+1;
length=end-start+1;
I=I/2;
}
else//奇數個球的處理方式
{
mid=length/2+start-1;
end=mid;
length=end-start+1;
I=(I+1)/2;
}
}
printf("%lld\n",a[start]);
}
}
}