Description

给定参数 $n, k(1 \leq n \leq 10^5, 0 \leq k \leq n$,构造一棵二叉树,满足:

  • 该二叉树有 $n$ 个节点
  • 不存在只有一个儿子的节点
  • 对于一个非叶子节点,记他的两个儿子对应子树的大小为 $A,B$。如果 $2A\leq B$ 或者 $2B \leq A$,那么这个点是“不平衡点”。你构造的二叉树需要恰好有 $k$ 个“不平衡点”。

Tutorial

挺牛逼的一个构造题,但细节有点多。

首先每个点儿子数量是 0 或者 2,那么点数必然为奇数。

然后考虑两种极端,即给定 $n$ 个点能够达成的最小的 $k$ 和最大的 $k$。

最大的 $k$ 显然是造一条毛毛虫,$\max k = \dfrac{n-3}{2}$。

最小的 $k$ 是造一棵完全二叉树,然后不难发现造出的是满二叉树时 $k=0$,不然 $k=1$。

考虑如何证明这件事情:

首先一个点左右子树的深度差至多为 $1$,然后要满足 $2A \leq B$ 必然有 $A = 2^{k-1}-1$,$B=2^k-1$,所以不平衡的点必然满足左右儿子树都是满二叉树然后深度差为 $1$。

显然这样的点在完全二叉树上最多只有一个。

然后对任意 $k$ 的构造方法也不难了,先造一个 $k-1$ 个不平衡点的毛毛虫,然后在下面挂一个完全二叉树。

这样做如果那个完全二叉树是满二叉树会出问题,于是考虑把最下面的某两个点接到根的那个没有儿子的儿子下面,在 $n$ 足够大的时候根依然是不平衡的,并且满二叉树上多了一个不平衡点。

最后这样做有两个 border case:

  • $n = 2^p-1 (p \in \mathbf{N}),k=1$,这样无解
  • $n = 9, k = 2$,这里把两个儿子拼上去以后根变得平衡了,枚举所有可能以后发现这样也是无解。

Code

#include <stdio.h>
#include <stdlib.h>
void no_answer() __attribute__ ((noreturn));
void no_answer() 
{
 puts("NO");
 exit(0);
}
#define N 100005
int fa[N];
int main()
{
 int n, k;
 scanf("%d%d", &n, &k);
 int limit = (n - 3) / 2;
 if (limit < 0) limit = 0;
 if (n % 2 ==0) no_answer();
 if (k > limit) no_answer();
 if ((n & (n+1)) && k ==0) no_answer();
 if (!(n & (n+1)) && k == 1) no_answer();
 if (n == 9 && k == 2) no_answer();
 puts("YES");
 int base = 2 * (k - 1);
 if (base < 0) base = 0;
 for (int i=1; i<base; i += 2)
 {
   fa[i] = i - 2;
   fa[i + 1] = i;
 }
 fa[base + 1] = base - 1;
 for (int i=base+2; i<=n; ++i) 
 {
   fa[i] = base + (i - base) / 2;
 }
 if (!((n - base) & (n - base + 1)) && k)
 {
   fa[n] = 2;
   fa[n - 1] = 2;
 }
 if (fa[1] < 0)
   fa[1] = 0;  
 for (int i=1; i<=n; ++i)
   printf("%d%c", fa[i], " \n"[i == n]);
 return 0;
}



Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.