嘘~ 正在从服务器偷取页面 . . .

CF1311E Construct the Binary Tree 题解


CF1311E Construct the Binary Tree 题解

题目链接:CF1311E Construct the Binary Tree

题意

要求构造一个 $n$ 个节点的二叉树(每个节点拥有不超过 $2$ 个孩子),节点 $1$ 为根,要使所有节点到根的距离之和为 $d$ 。要求先判断可不可以构造,如果可以输出 YES ,下一行输出 2n 号节点的父亲节点,否则输出 NO 。有多组询问。

输入格式

The first line of the input contains one integer $t$ ( $1 \le t \le 1000$ ) — the number of test cases.

The only line of each test case contains two integers $n$ and $d$ ( $2 \le n, d \le 5000$ ) — the number of vertices in the tree and the required sum of depths of all vertices.

It is guaranteed that the sum of $n$ and the sum of $d$ both does not exceed $5000$ ( $\sum n \le 5000, \sum d \le 5000$ ).

输出格式

For each test case, print the answer.

If it is impossible to construct such a tree, print NO in the first line. Otherwise, print YES in the first line. Then print $n-1$ integers $p_2, p_3, \dots, p_n$ in the second line, where $p_i$ is the parent of the vertex $i$ . Note that the sequence of parents you print should describe some binary tree.

这里讲一种线性的构造方法。详见参考文献 [1]

首先能构造的最小值一定是棵满二叉树的形式(国内定义),而最大值一定是一条链。

更一般的情况,若合法,则一定可以从满二叉树通过向链调整得到。

为啥呢?因为合法啊,不然你觉得长啥样啊

于是我们先构造最小值,记 $r$ 为剩余的要增加的深度,链的最下端为 $p$ 。

然后每次将编号最大的且不在主链上的点 $i$ (钦定 $2^k(k\in \mathbb{N})$ 为主链)插到主链上

若 $r \le (\mathtt{dep}(p) + 1)-\mathtt{dep}(i)$ ,那么 $i$ 需要成为 $p$ 的第 $\mathtt{dep}(p) + 1 - \mathtt{dep}(i) - r$ 个父亲

然后令 $r \gets r-(\mathtt{dep}(p) + 1-\mathtt{dep}(i))$ ,继续考虑下一个点。

时间复杂度 $\mathcal{O}(n)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(2e5+15))

int n,sum,fa[N],dep[N];
void pt() { for(int i=2; i<=n; i++) cout << fa[i] << " \n"[i==n]; }
void work()
{
    cin >> n >> sum; int cur = 0, pos; dep[0] = -1; 
    for(int i=1; i<=n; i++) 
    {
        dep[i] = dep[fa[i] = i >> 1] + 1; cur += dep[i];
        if((i & (i-1)) == 0) pos = i;
    }
    sum -= cur;
    if(sum < 0) { return cout << "NO\n", void(0); }
    if(sum == 0) { return cout << "YES\n", pt(); }
    for(int i=n; i; i--)
    {
        if((i & (i-1)) == 0) continue;
        sum -= dep[pos] + 1 - dep[i];
        if(sum <= 0)
        {
            while(sum != 0) pos = fa[pos], ++sum;
            sum = 0; fa[i] = pos; break;
        }
        fa[i] = pos; dep[i] = dep[pos] + 1; pos = i;
    }
    if(sum) cout << "NO\n";
    else
    { cout << "YES\n", pt(); }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int _Q; cin >> _Q; while(_Q--) work();
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/c2522943959/solution-cf1311e


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录