Binary Tree HDU - 5573(构造+思维)

Binary Tree HDU - 5573

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.

1T100 1 ≤ T ≤ 100 .

1N109 1 ≤ N ≤ 10 9 .

N2K260 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 +
题意:

有一棵无限的二叉树,根结点值为 12i,2i+1nk 1 , 左 子 结 点 为 2 i , 右 子 结 点 为 2 i + 1 , 给 出 n 和 k , 求在二叉树上从根向下共走 k k 步(层),每步可以为当前结点权值取正号或者负号,求一种使最终取值和为n的可行方案(题目保证了 k60 k ≤ 60

分析:

根据题意我们这道这棵树是这样的形式
这里写图片描述

我们发现树的最左侧的所有节点为

20,21,22...2k1 2 0 , 2 1 , 2 2 . . .2 k − 1

转化成2进制形式的话我们知道其实这些节点上数字可以构成 12k1 1 到 2 k − 1 的数

但是题目并不是要求选取,而是对每个点的值只能执行加或者减操作

我们先把左侧节点都加起来得到sum,因为根节点为1,其他都是偶数,因此如果我们把左侧节点全加起来得到的数一定是奇数,如果要求的N是奇数,那就先把左边都加起来,如果N是偶数的话最后一层我们不加左孩子节点了,而是加 2k1+1 2 k − 1 + 1 ,也就是最后一层的右孩子节点的值,这样就能满足暂时得到的sum和N的奇偶性相同并且 Nsum N ≤ s u m

因此我们看一下sum到底要比我们要求的N多多少,想办法构造消去这个多的部分就行

假设差值为d

d=sumN d = s u m − N

因为我们之前保证了sum和N的奇偶性相同那么我们得到的差值d一定是偶数

sum=N+d=N+d2+d2 s u m = N + d = N + d 2 + d 2

发现多了d,如果在加的过程中不加d,就好了,但是那是不可能的,因为题目要求要么加要么减,不可能不加也不减。

这个时候因为d是偶数所以,如果我们假设能够构造出 d2 d 2 ,那么我们想消去d的话只需要构造一个 d2 − d 2 ,这样式子变成了

sumd2=N+d2d2=N s u m − d 2 = N + d 2 − d 2 = N

发现神奇哎就得到了N

那么怎么构造呢,可能感觉很麻烦,但是其实并不,真的是非常的巧妙

我们把 d2 d 2 这个数写成2进制的话,我们知道位为1那么我们就选取,0的话就不选。

但是这里我们只需要减去一个 d2 d 2 ,实际上我们只需要减去 d2 d 2 二进制位上的1就行了,其他的都是加

因此求解过程就是:

首先求出sum

然后求出 d2 d 2

然后遍历 d2 d 2 的二级制位,为1,到了当前节点就是-,否则就都是+

我觉得这东西我死都想不出来。。。

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int T,cas = 0;
ll n,k;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld",&n,&k);
        printf("Case #%d:\n",++cas);
        ll sum = 0,num = 1;
        for(int i = 1; i < k; i++){
            sum += num;
            num <<= 1;
        }
        if(n % 2 == 0) sum += (num + 1);
        else sum += num;
        ll d = (sum - n) / 2;
        num = 1;
        while(k > 1){//遍历每层每个点的时候看d的二级制位是0或1决定+-号
            printf("%d ",num);
            if((d & 1) == 0) printf("+\n");
            else printf("-\n");
            d >>= 1;
            num <<= 1;
            k--;
        }
        if(n % 2 == 0) printf("%d ",num+1);
        else printf("%d ",num);
        if((d & 1) == 0) printf("+\n");
        else printf("-\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值