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