📜  查询节点的子树中值小于V的节点数

📅  最后修改于: 2021-04-17 09:29:44             🧑  作者: Mango

给定一个由N个节点和Q个查询组成的根树(假定根为1 ),每种形式为(Val,Node) 。对于每个查询,任务是在Node的子树(包括其自身)中查找值小于Val的节点数。
请注意,根据定义,该树中的节点是唯一的。
例子:

Input: N = 7, Q = 3
Val = 4, Node = 4
Val = 7, Node = 6
Val = 5, Node = 1
Given tree:

Output: 2
1
4
Explanation: For given queries:
Q1 -> Val = 4, Node = 4
  The required nodes are 2 and 3
  Hence the output is 2

Q2 -> Val = 7, Node = 6
  The required node is 6 only
  Hence the output is 1

Q3 -> Val = 5, Node = 1
  The required nodes are 1, 2, 3 and 4
  Hence the output is 4

天真的方法:解决此问题的一种简单方法是为每个查询从给定节点运行DFS,并计算小于给定值的节点数。给定节点的父级必须从DFS中排除。
时间复杂度: O(N * Q) ,其中Q是查询数, N是树中节点的数。
高效的方法:我们可以减少在子树中找到元素数量的麻烦,而不必在数组的相邻段中找到它们。为了生成这样的表示,我们从根节点运行一个DFS,并在第一次进入节点和最后一次退出节点时将其推入数组。一棵树的这种表示方式被称为“ Euler Tour of the tree”。
例如,

上面那棵树的欧拉之旅将是:

1 4 2 2 3 3 5 5 4 6 7 7 6 1

树的这种表示具有以下属性:每个节点X的子树都包含在数组中X的第一个和最后一个出现的位置内。每个节点正好出现两次。因此,计算节点的首次和最后出现的节点之间除Val小的数目将会给我们查询的两倍答案。
使用此表示,可以使用二进制索引树在每个查询中以O(log(N))脱机处理查询。
预处理:

  1. 我们将Tour中每个节点的第一个和最后一个出现的索引存储在两个数组中,即startend 。让start [X]end [X]代表节点X的这些索引。这可以在O(N)中完成
  2. 在Euler Tour中,我们将元素的位置与节点一起存储为一对(indexInTree,indexInTour) ,然后根据indexInTree进行排序。让这个数组排序
  3. 同样,我们维护一系列形式为(Val,Node)的查询,并根据Val进行排序。让这个数组进行sortedQuery
  4. 初始化大小为2N的二进制索引树中的所有条目为0让这成为
  5. 然后进行如下操作。在sortedToursortedQuery中分别维护一个指针
  6. 对于从头开始的sortedQuery中的每个查询,从sortedTour选择具有indexInTree 的节点,并以bit为单位递增其indexInTour 。那么该查询的答案将是从start [Node]end [Node]的总和的一半
  7. 对于sortedQuery中的下一个查询,我们从sortedTour中选择任何先前未选择的具有indexInTree 节点,并按位递增其indexInTour并像以前一样回答查询。
  8. 对每个查询重复此过程,我们可以在O(Qlog(N))中回答它们。

以下是上述方法的C++实现:

CPP
// C++ program Queries to find the Number of
// Nodes having Smaller Values from a Given
// Value in the Subtree of a Node
#include 
using namespace std;
   
// Takes a tree and generates a Euler tour for that
// tree in 'tour' parameter This is a modified form
// of DFS in which we push the node while entering for
// the first time and when exiting from it
void eulerTour(vector >& tree,
               vector& vst,
               int root,
               vector& tour)
{
   
    // Push node in tour while entering
    tour.push_back(root);
   
    // DFS
    vst[root] = 1;
    for (auto& x : tree[root]) {
        if (!vst[x]) {
            eulerTour(tree, vst, x, tour);
        }
    }
   
    // Push ndode in tour while exiting
    tour.push_back(root);
}
   
// Creates the start and end array as described in
// pre-processing section. Traverse the tour from
// left to right and update start for 1st occurrence
// and end for 2nd occurrence of each node
void createStartEnd(vector& tour,
                    vector& start,
                    vector& end)
{
    for (int i = 1; i < tour.size(); ++i) {
        if (start[tour[i]] == -1) {
            start[tour[i]] = i;
        }
        else {
            end[tour[i]] = i;
        }
    }
}
   
// Returns a sorted array of pair containing node and
// tourIndex as described in pre-processing section
vector >
createSortedTour(vector& tour)
{
    vector > arr;
    for (int i = 1; i < tour.size(); ++i) {
        arr.push_back(make_pair(tour[i], i));
    }
    sort(arr.begin(), arr.end());
    return arr;
}
   
// Binary Indexed Tree Implementation
// This function will add 1 from the position
void increment(vector& bit, int pos)
{
    for (; pos < bit.size(); pos += pos & -pos) {
        bit[pos]++;
    }
}
   
// It will give the range sum
int query(vector& bit,
              int start,
              int end)
{
    --start;
    int s1 = 0, s2 = 0;
    for (; start > 0; start -= start & -start) {
        s1 += bit[start];
    }
    for (; end > 0; end -= end & -end) {
        s2 += bit[end];
    }
    return s2 - s1;
}
  
// Function to calculate the ans for each query
map, int> cal(int N,
                                         int Q,
                      vector> tree,
            vector> queries)
{
    // Preprocessing
    // Creating the vector to store the tours and queries
    vector tour, vst(N + 1, 0),
        start(N + 1, -1),
        end(N + 1, -1),
        bit(2 * N + 4, 0);
   
    // For one based indexing in tour.
    // Simplifies code for Binary Indexed Tree.
    // We will ignore the 1st element in tour
    tour.push_back(-1);
   
    // Create Euler Tour
    eulerTour(tree, vst, 1, tour);
   
    // Create Start and End Array
    createStartEnd(tour, start, end);
   
    // Create sortedTour and sortedQuery
    auto sortedTour = createSortedTour(tour);
   
    auto sortedQuery = queries;
   
    sort(sortedQuery.begin(), sortedQuery.end());
   
    // For storing answers to query
    map, int> queryAns;
   
    // We maintain pointers each for sortedTour and
    // sortedQuery.For each element X in sortedTour
    // we first process any queries with val smaller
    // than X's node and update queryptr to first
    // unprocessed query.Then update the position
    // of X in BIT.
   
    int tourptr = 0, queryptr = 0;
    while (queryptr < sortedQuery.size()) {
   
        // Queries lies in the range then
        while (queryptr < sortedQuery.size()
               && sortedQuery[queryptr].first
                      <= sortedTour[tourptr].first){
   
            auto node = sortedQuery[queryptr].second;
   
            // Making the query on BIT and dividing by 2.
            queryAns[sortedQuery[queryptr]]
                = query(bit, start[node], end[node]) / 2;
            ++queryptr;
        }
        if (tourptr < sortedTour.size()) {
            increment(bit, sortedTour[tourptr].second);
            ++tourptr;
        }
    }
  
    return queryAns;
}
  
// Driver Code
int main()
{
  
    int N = 7, Q = 3;
   
    // Tree edges
    vector > tree = { {},
                                      { 4, 6 },
                                      { 4 },
                                      { 4 },
                                      { 1, 2, 3, 5 },
                                      { 4 },
                                      { 1, 7 },
                                      { 6 } };
   
    // Queries vector
    vector > queries
        = { make_pair(4, 1),
            make_pair(7, 6),
            make_pair(5, 1) };
   
    // Calling the function
    map, int> queryAns =
                    cal(N, Q, tree, queries);
  
    // Print answer in order of input.
    for (auto& x : queries) {
        cout << queryAns[x] << '\n';
    }
   
    return 0;
}


输出:
3
1
4