作者:手机用户2702935897 | 来源:互联网 | 2023-10-11 19:45
http:acm.split.hdu.edu.cnshowproblem.php?pid5573题意:满二叉树,节点x左儿子标号是2*x,右儿子标号是2*x+1,每个节点权值即
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
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<>1;
printf("Case #%d:\n",tca++);
for(int i=0;i