7401 - Binary Tree
Time limit: 3.000 seconds
The Old Frog King lives on the root of an infinite tree. According to the law, each node should connect
to exactly two nodes on the next level, forming a full binary tree.
Since the king is professional in math, he sets a number to each node. Specifically, the root of the
tree, where the King lives, is 1. Say froot = 1.
And for each node u, labels as fu, the left child is fu × 2 and right child is fu × 2 + 1. The king
looks at his tree kingdom, and feels satisfied.
Time flies, and the frog king gets sick. According to the old dark magic, there is a way for the king
to live for another N years, only if he could collect exactly N soul gems.
Initially the king has zero soul gems, and he is now at the root. He will walk down, choosing left or
right child to continue. Each time at node x, the number at the node is fx (remember froot = 1), he
can choose to increase his number of soul gem by fx, or decrease it by fx.
He will walk from the root, visit exactly K nodes (including the root), and do the increasement or
decreasement as told. If at last the number is N, then he will succeed.
Noting as the soul gem is some kind of magic, the number of soul gems the king has could be
negative.
Given N, K, help the King find a way to collect exactly N soul gems by visiting exactly K nodes.
Input
First line contains an integer T, which indicates the number of test cases.
Every test case contains two integers N and K, which indicates soul gems the frog king want to
collect and number of nodes he can visit.
Restrictions:
• 1 ≤ T ≤ 100.
• 1 ≤ N ≤ 109
.
• N ≤ 2^K ≤ 2^60
.
Output
For every test case, you should output ‘Case #x:’ first, where x indicates the case number and counts
from 1.
Then K lines follows, each line is formated as ‘a b’, where a is node label of the node the frog visited,
and b is either ‘+’ or ‘-’ which means he increases / decreases his number by a.
It’s guaranteed that there are at least one solution and if there are more than one solutions, you
can output any of them.
Sample Input
2
5 3
10 4
Sample Output
Case #1:
1 +
3 -
7 +
Case #2:
1 +
3 +
6 -
12 +
区赛死在这个题目,耿耿于怀。
题目意思很简单 给一颗满二叉树 然后要求给出一条到第K层的路径
通过路径上的节点加或者减 最后得到N
很明显是构造题。
如果这个题目K和N是随便给的,想必真的会困难一些。
但是这个题目给了一个非常关键的限定N ≤ 2^K ≤ 2^60
K小于60这个条件可以解读为题目中数据范围 longlong就够了
重点在于N ≤ 2^K
注意到 第K层所能得到的最大数(最右边一路经从上到下完全加起来)是远远大于这个值的
也就是说可能根本就用不到那么大的范围就可以得到想要的数
最左侧一路径的数加起来是2^K-1
而恰好这一列通过加减的改变可以表示所有这一范围内的所有奇数
那么事实上只需要在最后一层选择左数第二个数那么这一路径就可以表示2^K内的所有偶数
因此题目不用赘述保证有解 这个条件已经够了。
至于确定符号其实从最下面的数贪心就可以了。
因为从最下面看 如果一个数从正变成了负 那么所有上面的数字变号都不足以抵消这个变化
由于N是个正数,因此最下面一层的数字为正 然后N减掉这个数
结果是正的就继续减掉下个数,结果是负的就加上下个数。加的时候实际符号对应减,反之对应加。
得到0的时候刚好处理完顶层1。
代码如下。
#include <iostream>
#include <stdio.h>
using namespace std;
typedef long long LL;
const int M=100;
LL ans[M]; //记录路过的数字
int ansp[M]; //记录对应的符号
LL er[M];
void init() //预处理2的幂次
{
er[1]=1;
for(int i=2;i<M;i++)
er[i]=er[i-1]*2;
}
int main()
{
int T;
int N,K;
init();
scanf("%d",&T);
for(int ca=1;ca<=T;ca++)
{
scanf("%d%d",&N,&K);
LL n=N;
LL now=er[K]+1;
if(N&1)
now--; //处理最下层
int p=0;
while(n!=0)
{
//cout<<now<<" "<<n<<endl;
if(n>0)
{
n-=now;
ans[p]=now;
ansp[p]=1;
}
else
{
n+=now;
ans[p]=now;
ansp[p]=0;
}
now/=2;
p++;
}
printf("Case #%d:\n",ca);
for(int i=p-1;i>=0;i--)
printf("%I64d %c\n",ans[i],ansp[i]==0?'-':'+');
}
return 0;
}