CF1311E - Construct the Binary Tree
题意
构造 N N N个点,所有点深度之和为 D D D的二叉树
题解
考虑以下两种情况
①
N
N
N个点深度之和最小
这种就是每一层都是满的,这个等等算
②
N
N
N个点深度之和最大
链状,
N
N
N个点组成直线,深度之和为
N
∗
(
N
−
1
)
/
2
N*(N-1)/2
N∗(N−1)/2
那么我们现在要构造一颗深度之和为
D
D
D的二叉树
那么这个深度
m
i
n
≤
D
≤
m
a
x
min\leq D \leq max
min≤D≤max才有解
这里有两个方向
m
a
x
max
max ->
m
a
x
−
1
max-1
max−1,从最大的开始,每次减一
m
i
n
min
min ->
m
i
n
+
1
min+1
min+1,从最小的开始,每次加一
这里是第二种方向
我们先确定每一层二叉树的点数量
然后根据关系式,上一层点数*2>=当前层点数
如果上一层减一个仍然满足这个式子,那就从上一层拿一个放到当前层
代码
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define sz sizeof
#define eps 1e-9
#define lowbit(x) x&-x
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MAX = 5e3 + 10;
int T, N, D;
int num[MAX], ans[MAX];
void solve() {
if (D > (N - 1) * N / 2) {
printf("NO\n");
return;
}
memset(num, 0, sz(num));
//初始状态的构建
num[0] = 1;
for (int i = 2; i <= N; i++)//将N-1个点放满二叉树
for (int j = 1; j <= N; j++)
if (num[j] + 1 <= 2 * num[j - 1]) {//当前层多一个点满足式子,我就多一个
num[j]++;
D -= j;
break;
}
if (D < 0) {
printf("NO\n");
return;
}
while (D--) {
for (int i = 1; i <= N; i++)
if (num[i] + 1 <= 2 * (num[i - 1] - 1)) {//当前层多一个点,上面层少一个仍然满足式子
//将下移动一个到上
num[i]++, num[i - 1]--;
break;
}
}
printf("YES\n");
int tot = 1, now = 1;
for (int i = 1; i <= N; i++) {
int pre = now + 1;
for (int j = 0; j < num[i]; j++)
ans[++now] = tot + j / 2;
tot = pre;
}
for (int i = 2; i <= N; i++)
printf("%d%s", ans[i], i == N ? "\n" : " ");
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &N, &D);
solve();
}
return 0;
}