hdu5573Binary Tree 思维构造

55 篇文章 0 订阅

http://acm.split.hdu.edu.cn/showproblem.php?pid=5573

题意:满二叉树,节点x左儿子标号是2*x,右儿子标号是2*x+1,每个节点权值即标号,从1号节点开始,走一段长度为k的路径,自己选择每个节权值加或减,问最后能不能凑成n

想了很久都没思路,甚至还想到了打表找规律,徒劳。。。。。。

看了题解,发现。。。。。。

。。。。。稍微拐个歪就能想到啊。。。

题目保证了n<=2^k

这样我们发现仅需要最左边这条链就可以了

1,2,4,8,16,。。。

容易想到二进制拆分,但由于定义的原因,每个节点必须选择加或是减,直接对n拆分不可行,在仔细考虑一个加号变为减号,少掉的是这个节点值的二倍

为什么要选择最左边这条链呢,因为权值代表了二进制的每一位,可以组合成范围内的任意数,那么我先把所有都置为正数,结果是2^k-1,当n比他小时,是不是只需要减掉一定的数就可以了,而刚刚已经分析了把一个节点置为负,是要减掉权值的二倍,这样我们只能减去一个偶数,可能还会剩1,怎么办呢,会发现只需要判断下奇偶改一下最后一个节点就可以了,把最后一个节点选右儿子,可以比选左儿子多1,上面减时在多减一个2,不就把这个1去掉了

当n比他大时,似乎只有n=2^k一个情况,最后一个选右儿子即可

代码超短

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T,n,k,tca=1;
    char ch[2]={'+','-'};
    cin>>T;
    while(T--)
    {
        scanf("%d%d",&n,&k);
        long long sum=(1ll<<k)-1;
        long long temp=(sum-n+1)>>1;
        printf("Case #%d:\n",tca++);
        for(int i=0;i<k-1;i++)
            printf("%lld %c\n",1ll<<i,ch[(bool)(temp&(1ll<<i))]);
        printf("%lld +\n",(1ll<<(k-1))+!(n&1));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值