给定一个由N 个整数组成的数组arr[] ,任务是执行以下两个查询:
- query(start, end) : 从头到尾打印子数组中斐波那契数的个数
- update(i, x) :将 x 添加到数组索引i 所引用的数组元素,即: arr[i] = x
例子:
Input: arr = { 1, 2, 3, 4, 8, 9 }
Query 1: query(start = 0, end = 4)
Query 2: update(i = 3, x = 5)
Query 3: query(start = 0, end = 4)
Output:4
5
Explanation
In Query 1, the subarray [0…4] has 4 fibonacci numbers viz. {1, 2, 3, 8}
In Query 2, the value at index 3 is updated to 5, the array arr now is, {1, 2, 3, 5, 8, 9}
In Query 3, the subarray [0…4] has 5 fibonacci numbers viz. {1, 2, 3, 5, 8}
方法:为了处理点更新和范围查询,段树最适合此目的。
为了检查斐波那契数,我们可以使用动态规划构建一个哈希表,其中包含所有小于或等于最大值 arr i的斐波那契数。我们可以使用 MAX 来测试时间复杂度为 O(1) 的数字。
构建段树:
- 现在使用线段树问题将问题简化为子数组和。
- 现在,我们可以构建段树,其中叶节点表示为 0(如果它不是素数)或 1(如果它是斐波那契数)。
- 线段树的内部节点等于其子节点的总和,因此一个节点表示从 L 到 R 范围内的总斐波那契数,范围 [L, R] 落入该节点及其下的子树。
处理查询和点更新:
- 每当我们收到一个从头到尾的查询时,我们可以查询段树中从头到尾范围内的节点总和,这反过来又代表了从头到尾范围内的斐波那契数。
- 要执行点更新并将索引 i 处的值更新为 x,我们检查以下情况:
令 arr i的旧值为 y,新值为 x。- 案例 1:斐波那契:如果 x 和 y 都是斐波那契数
子数组中斐波那契数的计数不会改变,所以我们只更新数组而不修改线段树 - 情况 2:如果 x 和 y 都不是斐波那契数
子数组中斐波那契数的计数不会改变,所以我们只更新数组而不修改线段树 - 情况 3:如果 y 是斐波那契数,但 x 不是
子数组中斐波那契数的计数减少,因此我们更新数组并将 -1 添加到每个范围。要更新的索引 i 是段树中的一部分 - 案例 4:如果 y 不是斐波那契数而 x 是斐波那契数
子数组中斐波那契数的计数增加,因此我们更新数组并向每个范围加 1。要更新的索引 i 是段树中的一部分
- 案例 1:斐波那契:如果 x 和 y 都是斐波那契数
下面是上述方法的实现:
CPP
// C++ program to find number of fibonacci numbers
// in a subarray and performing updates
#include
using namespace std;
#define MAX 1000
// Function to create hash table
// to check Fibonacci numbers
void createHash(set& hash,
int maxElement)
{
int prev = 0, curr = 1;
hash.insert(prev);
hash.insert(curr);
while (curr <= maxElement) {
int temp = curr + prev;
hash.insert(temp);
prev = curr;
curr = temp;
}
}
// A utility function to get the middle
// index from corner indexes.
int getMid(int s, int e)
{
return s + (e - s) / 2;
}
// Recursive function to get the number
// of Fibonacci numbers in a given range
/* where
st --> Pointer to segment tree
index --> Index of current node in the
segment tree. Initially 0 is passed
as root is always at index 0
ss & se --> Starting and ending indexes of
the segment represented by current
node, i.e., st[index]
qs & qe --> Starting and ending indexes
of query range
*/
int queryFibonacciUtil(int* st, int ss,
int se, int qs,
int qe, int index)
{
// If segment of this node is a part
// of given range, then return
// the number of Fibonacci numbers
// in the segment
if (qs <= ss && qe >= se)
return st[index];
// If segment of this node
// is outside the given range
if (se < qs || ss > qe)
return 0;
// If a part of this segment
// overlaps with the given range
int mid = getMid(ss, se);
return queryFibonacciUtil(st, ss, mid, qs,
qe, 2 * index + 1)
+ queryFibonacciUtil(st, mid + 1, se,
qs, qe, 2 * index + 2);
}
// Recursive function to update
// the nodes which have the given
// index in their range.
/* where
st, si, ss and se are same as getSumUtil()
i --> index of the element to be updated.
This index is in input array.
diff --> Value to be added to all nodes
which have i in range
*/
void updateValueUtil(int* st, int ss,
int se, int i,
int diff, int si)
{
// Base Case:
// If the input index lies outside
// the range of this segment
if (i < ss || i > se)
return;
// If the input index is in range
// of this node, then update the value
// of the node and its children
st[si] = st[si] + diff;
if (se != ss) {
int mid = getMid(ss, se);
updateValueUtil(st, ss, mid, i,
diff, 2 * si + 1);
updateValueUtil(st, mid + 1, se,
i, diff, 2 * si + 2);
}
}
// Function to update a value in the
// input array and segment tree.
// It uses updateValueUtil() to update
// the value in segment tree
void updateValue(int arr[], int* st,
int n, int i,
int new_val,
set hash)
{
// Check for erroneous input index
if (i < 0 || i > n - 1) {
printf("Invalid Input");
return;
}
int diff, oldValue;
oldValue = arr[i];
// Update the value in array
arr[i] = new_val;
// Case 1: Old and new values
// both are Fibonacci numbers
if (hash.find(oldValue) != hash.end()
&& hash.find(new_val) != hash.end())
return;
// Case 2: Old and new values
// both not Fibonacci numbers
if (hash.find(oldValue) == hash.end()
&& hash.find(new_val) == hash.end())
return;
// Case 3: Old value was Fibonacci,
// new value is non Fibonacci
if (hash.find(oldValue) != hash.end()
&& hash.find(new_val) == hash.end()) {
diff = -1;
}
// Case 4: Old value was non Fibonacci,
// new_val is Fibonacci
if (hash.find(oldValue) == hash.end()
&& hash.find(new_val) != hash.end()) {
diff = 1;
}
// Update the values of nodes in segment tree
updateValueUtil(st, 0, n - 1, i, diff, 0);
}
// Return number of Fibonacci numbers
// in range from index qs (query start)
// to qe (query end).
// It mainly uses queryFibonacciUtil()
void queryFibonacci(int* st, int n,
int qs, int qe)
{
int FibonacciInRange
= queryFibonacciUtil(st, 0, n - 1,
qs, qe, 0);
cout << "Number of Fibonacci numbers "
<< "in subarray from "
<< qs << " to "
<< qe << " = "
<< FibonacciInRange << "\n";
}
// Recursive function that constructs
// Segment Tree for array[ss..se].
// si is index of current node
// in segment tree st
int constructSTUtil(int arr[], int ss,
int se, int* st,
int si, set hash)
{
// If there is one element in array,
// check if it is Fibonacci number
// then store 1 in the segment tree
// else store 0 and return
if (ss == se) {
// if arr[ss] is fibonacci number
if (hash.find(arr[ss]) != hash.end())
st[si] = 1;
else
st[si] = 0;
return st[si];
}
// If there are more than one elements,
// then recur for left and right subtrees
// and store the sum of the
// two values in this node
int mid = getMid(ss, se);
st[si] = constructSTUtil(arr, ss, mid, st,
si * 2 + 1, hash)
+ constructSTUtil(arr, mid + 1, se, st,
si * 2 + 2, hash);
return st[si];
}
// Function to construct a segment tree from given array.
// This function allocates memory for segment tree and
// calls constructSTUtil() to fill the allocated memory
int* constructST(int arr[], int n, set hash)
{
// Allocate memory for segment tree
// Height of segment tree
int x = (int)(ceil(log2(n)));
// Maximum size of segment tree
int max_size = 2 * (int)pow(2, x) - 1;
int* st = new int[max_size];
// Fill the allocated memory st
constructSTUtil(arr, 0, n - 1, st, 0, hash);
// Return the constructed segment tree
return st;
}
// Driver Code
int main()
{
int arr[] = { 1, 2, 3, 4, 8, 9 };
int n = sizeof(arr) / sizeof(arr[0]);
// find the largest node value in the array
int maxEle = *max_element(arr, arr + n);
// Creating a set containing all Fibonacci numbers
// upto the maximum data value in the array
set hash;
createHash(hash, maxEle);
// Build segment tree from given array
int* st = constructST(arr, n, hash);
// Query 1: Query(start = 0, end = 4)
int start = 0;
int end = 4;
queryFibonacci(st, n, start, end);
// Query 2: Update(i = 3, x = 5),
// i.e Update a[i] to x
int i = 3;
int x = 5;
updateValue(arr, st, n, i, x, hash);
// uncomment to see array after update
// for(int i = 0; i < n; i++)
// cout << arr[i] << " ";
// Query 3: Query(start = 0, end = 4)
start = 0;
end = 4;
queryFibonacci(st, n, start, end);
return 0;
}
Java
// Java program to find number of fibonacci numbers
// in a subarray and performing updates
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
class GFG
{
static final int MAX = 1000;
// Function to create hash table
// to check Fibonacci numbers
static void createHash(Set hash, int maxElement)
{
int prev = 0, curr = 1;
hash.add(prev);
hash.add(curr);
while (curr <= maxElement)
{
int temp = curr + prev;
hash.add(temp);
prev = curr;
curr = temp;
}
}
// A utility function to get the middle
// index from corner indexes.
static int getMid(int s, int e)
{
return s + (e - s) / 2;
}
// Recursive function to get the number
// of Fibonacci numbers in a given range
/*
* where st --> Pointer to segment tree index --> Index of current node in the
* segment tree. Initially 0 is passed as root is always at index 0 ss & se -->
* Starting and ending indexes of the segment represented by current node, i.e.,
* st[index] qs & qe --> Starting and ending indexes of query range
*/
static int queryFibonacciUtil(int[] st, int ss, int se,
int qs, int qe, int index)
{
// If segment of this node is a part
// of given range, then return
// the number of Fibonacci numbers
// in the segment
if (qs <= ss && qe >= se)
return st[index];
// If segment of this node
// is outside the given range
if (se < qs || ss > qe)
return 0;
// If a part of this segment
// overlaps with the given range
int mid = getMid(ss, se);
return queryFibonacciUtil(st, ss, mid,
qs, qe, 2 * index + 1)
+ queryFibonacciUtil(st, mid + 1, se,
qs, qe, 2 * index + 2);
}
// Recursive function to update
// the nodes which have the given
// index in their range.
/*
* where st, si, ss and se are same as getSumUtil() i --> index of the element
* to be updated. This index is in input array. diff --> Value to be added to
* all nodes which have i in range
*/
static void updateValueUtil(int[] st, int ss,
int se, int i,
int diff, int si)
{
// Base Case:
// If the input index lies outside
// the range of this segment
if (i < ss || i > se)
return;
// If the input index is in range
// of this node, then update the value
// of the node and its children
st[si] = st[si] + diff;
if (se != ss)
{
int mid = getMid(ss, se);
updateValueUtil(st, ss, mid, i,
diff, 2 * si + 1);
updateValueUtil(st, mid + 1, se, i,
diff, 2 * si + 2);
}
}
// Function to update a value in the
// input array and segment tree.
// It uses updateValueUtil() to update
// the value in segment tree
static void updateValue(int arr[], int[] st, int n,
int i, int new_val, Set hash)
{
// Check for erroneous input index
if (i < 0 || i > n - 1)
{
System.out.printf("Invalid Input");
return;
}
int diff = 0, oldValue;
oldValue = arr[i];
// Update the value in array
arr[i] = new_val;
// Case 1: Old and new values
// both are Fibonacci numbers
if (hash.contains(oldValue) &&
hash.contains(new_val))
return;
// Case 2: Old and new values
// both not Fibonacci numbers
if (!hash.contains(oldValue) &&
!hash.contains(new_val))
return;
// Case 3: Old value was Fibonacci,
// new value is non Fibonacci
if (hash.contains(oldValue) &&
!hash.contains(new_val))
{
diff = -1;
}
// Case 4: Old value was non Fibonacci,
// new_val is Fibonacci
if (!hash.contains(oldValue) &&
hash.contains(new_val))
{
diff = 1;
}
// Update the values of nodes in segment tree
updateValueUtil(st, 0, n - 1, i, diff, 0);
}
// Return number of Fibonacci numbers
// in range from index qs (query start)
// to qe (query end).
// It mainly uses queryFibonacciUtil()
static void queryFibonacci(int[] st, int n, int qs, int qe)
{
int FibonacciInRange = queryFibonacciUtil(st, 0,
n - 1, qs, qe, 0);
System.out.printf("Number of Fibonacci numbers in subarray from %d to %d = %d\n", qs, qe, FibonacciInRange);
}
// Recursive function that constructs
// Segment Tree for array[ss..se].
// si is index of current node
// in segment tree st
static int constructSTUtil(int arr[], int ss, int se,
int[] st, int si, Set hash)
{
// If there is one element in array,
// check if it is Fibonacci number
// then store 1 in the segment tree
// else store 0 and return
if (ss == se)
{
// if arr[ss] is fibonacci number
if (hash.contains(arr[ss]))
st[si] = 1;
else
st[si] = 0;
return st[si];
}
// If there are more than one elements,
// then recur for left and right subtrees
// and store the sum of the
// two values in this node
int mid = getMid(ss, se);
st[si] = constructSTUtil(arr, ss, mid, st,
si * 2 + 1, hash)
+ constructSTUtil(arr, mid + 1, se,
st, si * 2 + 2, hash);
return st[si];
}
// Function to construct a segment tree from given array.
// This function allocates memory for segment tree and
// calls constructSTUtil() to fill the allocated memory
static int[] constructST(int arr[], int n, Set hash)
{
// Allocate memory for segment tree
// Height of segment tree
int x = (int) (Math.ceil(Math.log(n) / Math.log(2)));
// Maximum size of segment tree
int max_size = 2 * (int) Math.pow(2, x) - 1;
int[] st = new int[max_size];
// Fill the allocated memory st
constructSTUtil(arr, 0, n - 1, st, 0, hash);
// Return the constructed segment tree
return st;
}
// Driver Code
public static void main(String[] args)
{
int arr[] = { 1, 2, 3, 4, 8, 9 };
int n = arr.length;
// find the largest node value in the array
int maxEle = Arrays.stream(arr).max().getAsInt();
// Creating a set containing all Fibonacci numbers
// upto the maximum data value in the array
Set hash = new HashSet<>();
createHash(hash, maxEle);
// Build segment tree from given array
int[] st = constructST(arr, n, hash);
// Query 1: Query(start = 0, end = 4)
int start = 0;
int end = 4;
queryFibonacci(st, n, start, end);
// Query 2: Update(i = 3, x = 5),
// i.e Update a[i] to x
int i = 3;
int x = 5;
updateValue(arr, st, n, i, x, hash);
// uncomment to see array after update
// for(int i = 0; i < n; i++)
// cout << arr[i] << " ";
// Query 3: Query(start = 0, end = 4)
start = 0;
end = 4;
queryFibonacci(st, n, start, end);
}
}
// This code is contributed by sanjeev2552
Number of Fibonacci numbers in subarray from 0 to 4 = 4
Number of Fibonacci numbers in subarray from 0 to 4 = 5
时间复杂度:每次查询和更新的时间复杂度为O(log n) ,构建段树的时间复杂度为O(n)
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live