【蓝桥】小蓝的疑问

1、题目

问题描述

小蓝和小桥上完课后,小桥回顾了课上教的树形数据结构,他在地上画了一棵根节点为 1 的树,并且对每个节点都赋上了一个权值 w i w_i wi。

小蓝对小桥多次询问,每次询问包含两个整数 x , k x,k x,k,表示询问节点为 x x x 的所有 k k k 层子节点中,最大值是多少。

我们称节点 v v v 为 x x x 的 k k k 层子节点满足:

  1. v v v 是 x x x 为根的子树中的节点。
  2. d e p v − d e p x = k dep_v - dep_x = k depv−depx=k, d e p v dep_v depv 为 v v v 在树中的深度。

例如:

{2, 3} 为 1 号点的 1 层子节点,{4, 5, 6, 7} 为 1 号点的 2 层子节点,{4, 6} 为 2 号点的 1 层子节点。

输入格式

第一行输入两个正整数 n , q n,q n,q,表示树的节点数量和询问数量。

第二行输入 n n n 个正整数 w 1 , w 2 , . . . , w n w_1, w_2, ..., w_n w1,w2,...,wn,表示每个点的权值。

接下来 n − 1 n-1 n−1 行,每行输入两个整数 v i , u i v_i, u_i vi,ui,表示存在一条由 v i v_i vi 指向 u i u_i ui 的边。

接下来 q q q 行,每行输入两个整数 x , k x, k x,k,表示询问 x x x 的 k k k 层子节点中,最大值是多少。

输出格式

输出 q q q 行,每行输出一个整数,表示每个询问的最大值。

样例输入

7 4
2 9 8 7 8 6 4
1 2
1 3
2 4
2 6
3 5
3 7
1 2
1 1
3 1
2 1

样例输出

8
9
8
7

说明

样例如下图:

数据范围

  • 1 ≤ v i , u i , k , x ≤ n ≤ 1 0 5 1 \le v_i, u_i, k, x \le n \le 10^5 1≤vi,ui,k,x≤n≤105
  • 1 ≤ w i ≤ 1 0 9 1 \le w_i \le 10^9 1≤wi≤109
  • 1 ≤ q ≤ 1 0 5 1 \le q \le 10^5 1≤q≤105
  • 数据保证是一棵以 1 为根的有向树,并且每次询问的 k k k 一定合法。

原题链接

小蓝的疑问

2、思路

考察数据结构(堆,线段树),图(DFS序),二分查找

  1. 离线做法:启发式合并 + 优先队列 ,同时对于每层的节点都维护一个大根堆,每次询问,查询堆中最大值。时间复杂度: O ( n l o g 2 n ) O(n log^2n) O(nlog2n)。
  2. 在线做法:DFS序 + 线段树(ST表)+ 二分查找 ,对每层按照 DFS 序相对顺序建立线段树(或者 ST 表),当查询到 u u u 时,通过二分找到 k k k 层的左右端点,查询最大值。时间复杂度: O ( n l o g n ) O(n log n) O(nlogn)。

3、代码

  • 强制合并
cpp 复制代码
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <queue>
#include <assert.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 100;
const int MOD = 998244353;

vector<int> G[N];
int n, q;
int w[N];
int Siv[N], Son[N], ans[N];

typedef pair<int, int> Pair;

vector<Pair> query[N];

priority_queue<Pair> Dep[N];

int DFN = 0, rdn[N], dfn[N], dep[N];
int vis[N];

int ddep[N];

void dfs(int u, int fa = 0, int dpt = 1) {
    ddep[u] = dpt;
    Siv[u] = 1;

    for (int v : G[u]) {
        if (v == fa) continue;
        dfs(v, u, dpt + 1);
        ddep[u] = max(ddep[u], ddep[v]);
        Siv[u] += Siv[v];
        if (Siv[v] > Siv[Son[u]]) Son[u] = v;
    }
}

void to_get_ans(int u, int fa = 0, int dpt = 1, bool clr = false) {
    dfn[u] = ++DFN;
    rdn[dfn[u]] = u;
    dep[u] = dpt;
    for (int v : G[u]) {
        if (v == fa || v == Son[u]) continue;
        to_get_ans(v, u, dpt + 1, false);
    }

    if (Son[u]) {
        to_get_ans(Son[u], u, dpt + 1, true);
    }
    

    int ed = dfn[u];
    if (Son[u]) ed = dfn[Son[u]] - 1;

    for (int i = dfn[u]; i <= ed; ++i) {
        int vv = rdn[i];
        Dep[dep[vv]].push({w[vv], vv});
        vis[vv] = true;
    }
    // cout << endl;

    for (Pair q : query[u]) {
        int k = q.first;
        assert(k + dpt <= ddep[u]);
        int id = q.second;
        while (Dep[k + dpt].size() && vis[Dep[k + dpt].top().second] == 0) {
            Dep[k + dpt].pop();
            
        }
        ans[id] = Dep[k + dpt].top().first;
    }

    if (!clr) {
        int ed = dfn[u] + Siv[u];
        for (int i = dfn[u]; i < ed; ++i) {
            vis[rdn[i]] = false;
        }
    }

}

void sol() {
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
        cin >> w[i];
    }   
    int u, v; 
    for (int i = 1; i < n; ++i) {
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1);
    int x, k;
    for (int i = 1; i <= q; ++i) {
        cin >> x >> k;
        query[x].push_back({k, i});
    }
    to_get_ans(1);
    for (int i = 1; i <= q; ++i) {
        cout << ans[i] << '\n';
    }
}

int main() {
    int T = 1;
    while (T--) {
        sol();
    }
    exit(0);
}
  • 面向对象
cpp 复制代码
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>
#include <queue>
#include <assert.h>
#include <cmath>

using namespace std;

typedef long long ll;
const int N = 1e5 + 100;

vector<int> G[N], val[N], dfsQ[N];
int w[N], n, q;
int DFN = 0, dfn[N], dep[N], Siv[N], MaxDpt = 0;

class RMQ_t {
public:
    RMQ_t(const vector<int>& init);
    ~RMQ_t();
    int query(int l, int r) const {
        int k = log2(r - l);
        return max(f[k][l], f[k][r - (1 << k)]);
    }
private:
    int **f;
    const int N, LOGN;
};

RMQ_t *res[N];

void dfs(int u, int fa = 0, int dpt = 1) {
    MaxDpt = max(MaxDpt, dpt);
    dfn[u] = ++DFN; dep[u] = dpt;
    Siv[u] = 1;
    val[dpt].push_back(w[u]);
    dfsQ[dpt].push_back(dfn[u]);
    for (int v : G[u]) {
        if (v == fa) continue;
        dfs(v, u, dpt + 1);
        Siv[u] += Siv[v];
    }
}

void sol() {
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
        cin >> w[i];
    }   
    int u, v, x, k; 
    for (int i = 1; i < n; ++i) {
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1);
    for (int i = 1; i <= MaxDpt; ++i) {
        res[i] = new RMQ_t(val[i]);
    }
    while (q--) {
        cin >> x >> k;
        int l = lower_bound(dfsQ[k + dep[x]].begin(),
                            dfsQ[k + dep[x]].end(),
                            dfn[x]) - dfsQ[k + dep[x]].begin();
        int r = lower_bound(dfsQ[k + dep[x]].begin(),
                            dfsQ[k + dep[x]].end(),
                            dfn[x] + Siv[x]) - dfsQ[k + dep[x]].begin();
        cout << res[k + dep[x]]->query(l,r) << endl;
    }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
    int T = 1;
    while (T--) {
        sol();
    }
    exit(0);
}

RMQ_t::RMQ_t(const vector<int>& init) : N(init.size()), LOGN(log2(init.size()) + 1) {
    f = new int*[LOGN];
    for (int i = 0; i < LOGN; ++i) {
        f[i] = new int[N];
    }
    for (int i = 0; i < N; ++i) {
        f[0][i] = init[i];
    }
    for (int i = 1; i < LOGN; ++i) {
        for (int j = 0; j + (1 << i) - 1 < N; ++j) {
            f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << (i - 1))]);
        }
    }
}

RMQ_t::~RMQ_t() {
    for (int i = 0; i < LOGN; ++i) {
        delete[] f[i];
    }
    delete[] f;
}
相关推荐
sweetheart7-717 小时前
LeetCode17. 电话号码的字母组合(2024秋季每日一题 59)
算法·深度优先·力扣·dfs
XuYueming1 天前
[NOIP2022] 比赛 随机排列 部分分
数学·线段树·题解·单调栈·洛谷·扫描线·二维数点·部分分·概率 & 期望
哪 吒3 天前
华为OD机试 - 打印机队列 - 优先队列(Java 2024 E卷 200分)
java·开发语言·华为od·优先队列
银氨溶液5 天前
力扣——113. 路径总和
数据结构·算法·leetcode·职场和发展·dfs·回溯
王老师青少年编程5 天前
CSP/信奥赛C++刷题训练:经典深搜例题(1):洛谷1605 :迷宫
c++·算法·dfs·深搜·csp·信奥赛
丶Darling.6 天前
代码随想录 | Day36 | 动态规划 :整数拆分&不同的二叉搜索树
c++·笔记·学习·算法·动态规划·dfs·记忆化搜索
OT.Ter8 天前
【力扣打卡系列】二分查找(搜索旋转排序数组)
算法·leetcode·职场和发展·go·二分查找
硕风和炜12 天前
【LeetCode:264. 丑数 II + 小根堆】
java·数学·算法·leetcode·优先队列·最小堆
C++oj18 天前
csp普及组算法集训--Dfs
c++·学习·数学·算法·dfs