P16779 ⌈Xzy OI R1 T1⌋ 查查编 题解

题目大意

有个编号由 \(a\) 至 \(b\) 的完全二叉搜索树,给定根节点的编号 \(x\) 和 \(a\),你需要求 \(b\) 的最大值。

完全二叉搜索树是满足以下性质的二叉树:

  • 对于每个节点,其左子树(如有)所有节点编号都小于该节点编号,其右子树(如有)所有节点编号都大于该节点编号。

  • 除最后一层外,每一层都是满的,且最后一层的节点全部集中在左侧。

算法分析

观察数据范围,容易发现 \(a≤x\),而 \(a\) 和 \(x\) 相同时,显然 \(b\) 和 \(a\) 必然也相等,这里加个特判。

然后自己多造几组数据就可以发现:根节点的左子树节点数量是固定的,且左子树大小为(不包含根节点)\(x-a\),再根据完全二叉搜索树的性质二,就能推断出右子树的大小,从而算出 \(b\) 的最大值。

就拿样例解释的那张图做解释,可以看到右子树很像左子树,只是左子树最后一层多了一个节点。为什么这样 \(b\) 是最大的呢?因为如果还想更大的话,就得在右子树增加点,而这样就会导致不满足性质二,所以不能再大了。

所以就能得出结论:右子树的大小最优一定为 \(2^k-1(k≥0)\),且右子树的大小一定不超过左子树的大小。

最后枚举 \(k\),找到一个最大的且 \(2^k-1≤x-a\),答案就是 \(x+2^k-1\)。

#include<bits/stdc++.h>
using namespace std;
int main(){
   std::ios::sync_with_stdio(0);
   cin.tie(0),cout.tie(0);
   int T;
   cin>>T;
   while(T--){
      long long a,x;
      cin>>a>>x;
      if(a==x){
         cout<<a<<'\n';
         continue;
      }
      long long xx=x-a;
      long long k=1;//这里k直接省略每次乘的过程,直接算
      while(true){//枚举k
         if(k-1ll>xx){//如果超过了
            k/=2ll;
            break;
         }
         k*=2ll;//否则继续枚举
      }
      cout<<x+k-1ll<<'\n';
   }
   return 0;
}
posted @ 2026-10-17 19:06  SCX2014  阅读(3)  评论(0)    收藏  举报