题目链接:点击查看
题目大意:给出一棵无限大的完全二叉树,每次给出一个 n 和一个 k ,要求从点 1 出发一直向下,找到一条长度为 k 的路径,对于路径上的点可以加上其编号,也可以减去其编号,需要构造出一种方案,使得计算的结果为 n
题目分析:因为 k 最大只有 60 ,给足了提示是需要往二进制上思考,但奈何对二进制的题目不太敏感,之前打 cf 时碰到的二进制题目也是因为不会做无奈只能掉分。。菜啊
回到这个题目,稍微画几层这个完全二叉树,不难发现最左边的一条链上恰好是二进制数,也就是 1 , 2 , 4 , 8 ... ,因为题目保证了 ,所以最左边一条链之和我们记为 sum,一定有 sum >= n
所以我们完全可以选择最左边的这条链,然后在适当的位置将正号变为负号即可,考虑变号的影响,对于一个位置 x ,原本 sum = 1 + 2 + 4 + ... + x + ... + 2^k, 现在如果将 x 前面的正号变为负号,其影响是 1 + 2 + 4 + ... - x + ... + 2^k = sum - 2 * x
可以看到,如果将一个位置的正号变为负号,总的 sum 会减少其两倍的贡献,换一种思想,如果我们想要让 sum 减少 x ( x 为偶数 )的话,那么我们只需要将编号为 x/2 的节点变号即可
接下来我们设置变量 delta = sum - n ,也就是说我们只需要将最左边的这条链,减少 delta 的量,就可以让其计算的结果等于 n 了,如果 delta 为偶数的话,上文也提到了,直接将其除以 2 ,然后找到相应的位置变号即可
现在考虑如果 delta 为奇数该如何处理,这个时候我们只需要在第 k - 1 层到达第 k 层时,遍历其右儿子而不是其左儿子就可以了,这样的话 sum 就变成了 sum + 1,此时再去计算 delta 的话就是偶数了,处理方法同上
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int w;
cin>>w;
int kase=0;
while(w--)
{
LL n,k;
scanf("%lld%lld",&n,&k);
LL sum=(1<<k)-1;
LL delta=sum-n;
bool flag=(delta&1);
delta=(delta+1)/2;
printf("Case #%d:\n",++kase);
for(int i=0;i<k-1;i++)
{
if((delta>>i)&1)
printf("%lld -\n",1LL<<i);
else
printf("%lld +\n",1LL<<i);
}
if(flag)
printf("%lld +\n",(1LL<<k-1)+1);
else
printf("%lld +\n",1LL<<k-1);
}
return 0;
}