hdu5537 2015ACM上海现场赛B题 构造题

题意

  • 给你两个数n和k。有一棵完全二叉树,他的节点编号id是根节点为1,左孩子2i,右孩子2i+1。
  • 你有一个数x,开始为0,从根节点出发,在一个节点上选择用x减去id或者加上id,然后可以去左孩子或者右孩子,每次加或减算一次操作,问你如何k次操作后使得x == n。
  • 1<=n <= 10^9
  • n <= 2^k <= 2^60

思路

  • 我们看最左边的一枝,第m层的节点id = 2^(m-1),如果每次可以选择加或者不加该层节点,很明显就是一个2进制数,k层的可以凑出1~2^k-1的所有数,如果第k层我们选择,最左边节点右边的第一个节点,所有数都加,是不是就可以得到2^k了。所以题目这样设计的话,我们已经构造出解了。
  • 但现在,题目是加或者减,这和刚才的问题有什么关联呢?保持其它节点的选择不变,看根节点,加或者不加,差为1,加或者减,差为2;类似的根节点的左孩子,加或者不加,差为2,加或者减差为4,这样我们就知道对于第i层的节点,加或不加,差为2^(i-1),加或者减差为2^i
  • 由上面的结论,我们不难想到,保持第K层节点为加,第一层到第K-1层,选择加或者减,可以凑出1~2^k-1中数的一半,而且是从1开始每次加2的数,很显然就是可以凑出1~2^k-1中所有的奇数。
  • 这样就简单了,当n是偶数时,只要把n-1,按上面的方法做,但第k层我们选择,最左边节点右边的第一个节点,这样所有的n都可以构造出来了。

吐槽

  • 比赛时没有注意到n <= 2^k。。。回来分分钟就A了。。。真是tmd日了狗了。。。。

实现

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;

int main(){
    int n,T,k;
    cin>>T;
    for (int t=1;t<=T;t++){
        cin>>n>>k;
        printf("Case #%d:\n",t);
        ll N = n;
        ll j = 1;
        if ((n & 1) == 0)
            N -= 1;
        N >>= 1;
        for (int i=1;i<k;i++){
            cout<<j<<' ';
            if (N & 1){
                puts("+");
            }
            else{
                puts("-");
            }
            j <<= 1;
            N >>= 1;
        }
        if (n & 1)
            cout << j << " +\n";
        else
            cout << j+(ll)1 << " +\n";
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值