Subsegments(构造)

58 阅读2分钟

E-Subsegments (nowcoder.com)

题目描述

树的节点有黑色和白色两种,黑色节点的所有子结点都为黑色。

要求构造一棵初始节点没有颜色的树,对树进行所有满足以上条件的涂色方式共有 kk 种。

输入格式

输入 k(2k21018)k(2\le k \le 2\cdot 10^{18})

输出格式

第一行输出一个数字 nn ,代表节点数。

接下来 n1n-1 行,输入两个数字 u,vu,v 代表一条连接节点 uu 和节点 vv 的边。

题目分析

这是一道 构造题

首先对于每个节点,他有黑色和白色两种状态,若其为黑色,则其子节点只能为黑色。

我们列举样例进行分析,首先对于每个叶节点,我们给其赋初值 22,给其他节点赋初值 11。一个节点的最终权值为其自身权值加上其直接子节点的权值的乘积,一棵树的权值为其根节点的权值。每个节点的权值也就是以这个节点为根的满足条件的涂色方式。

现在我们要进行上述涂色方式的逆过程,首先我们创造根节点并判断是否还需要继续向下拓展。若其需要继续向下,则 k1k-1 并进入下一层 。对于当前层,若 kk 可以被 22 整除,则需要添加一个当前层的叶节点,然后 k/=2k /= 2。知道 kk 为奇数为止,若 kk11 ,则证明构造完毕,否则 k=1k-=1 并创造一个新节点,以这个新节点为下一层的根节点,进入下一层。依次向下直至构造完毕。

可以大致判断最终层数不超过 100100。时间复杂度为 O(logn)O(logn)

Accept 代码

#include <bits/stdc++.h>
#define int long long

using namespace std;

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int k; cin >> k;
    vector<int> h(100);
    h[1] = 1, k --;
    int u = 2, m = 0, n = 1;
    while (k > 1)
    {
        if (k % 2 == 0) m ++, k /= 2;
        else
        {
            k --;
            m ++;
            h[u ++] = m;
            n += m;
            m = 0;
        }
        if (k == 1) h[u ++] = m, n += m;
    }
    
    cout << n << "\n";
    int root = 1;
    for (int i = 2; i < u; i ++)
    {
        for (int j = 1; j <= h[i]; j ++)
            cout << root << ' ' << root + j << "\n";
        root += h[i];
    }
    return 0;
}