我们有一个数组arr [0。 。 。 n-1]。查询有两种类型
- 查找元素从索引l到r的XOR,其中0 <= l <= r <= n-1
- 将数组的指定元素的值更改为新值x。我们需要做arr [i] = x,其中0 <= i <= n-1。
总共将有q个查询。
输入约束
n <= 10^5, q <= 10^5
解决方案1
一个简单的解决方案是运行从l到r的循环,并计算给定范围内元素的异或。要更新值,只需做arr [i] = x。第一次操作花费O(n)时间,第二次操作花费O(1)时间。最坏情况下,q个查询的时间复杂度为O(n * q)
n〜10 ^ 5和q〜10 ^ 5将花费大量时间。因此,此解决方案将超过时间限制。
解决方案2
另一种解决方案是将xor存储在所有可能的范围内,但存在O(n ^ 2)个可能范围,因此,在n〜10 ^ 5时,它将超过空间复杂度,因此,如果不考虑时间复杂度,我们可以说此解决方案将不起作用。
解决方案3(段树)
先决条件:段树
我们构建给定数组的分段树,使数组元素位于叶子处,内部节点存储在其下覆盖的叶子的XOR。
C
// C program to show segment tree operations like construction,
// query and update
#include
#include
#include
// A utility function to get the middle index from corner indexes.
int getMid(int s, int e) { return s + (e -s)/2; }
/* A recursive function to get the xor of values in given range
of the array. The following are parameters for this function.
st --> Pointer to segment tree
si --> 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[si]
qs & qe --> Starting and ending indexes of query range */
int getXorUtil(int *st, int ss, int se, int qs, int qe, int si)
{
// If segment of this node is a part of given range, then return
// the xor of the segment
if (qs <= ss && qe >= se)
return st[si];
// 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 getXorUtil(st, ss, mid, qs, qe, 2*si+1) ^
getXorUtil(st, mid+1, se, qs, qe, 2*si+2);
}
/* A recursive function to update the nodes which have the given
index in their range. The following are parameters
st, si, ss and se are same as getXorUtil()
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);
}
}
// The function to update a value in 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)
{
// Check for erroneous input index
if (i < 0 || i > n-1)
{
printf("Invalid Input");
return;
}
// Get the difference between new value and old value
int diff = new_val - arr[i];
// Update the value in array
arr[i] = new_val;
// Update the values of nodes in segment tree
updateValueUtil(st, 0, n-1, i, diff, 0);
}
// Return xor of elements in range from index qs (quey start)
// to qe (query end). It mainly uses getXorUtil()
int getXor(int *st, int n, int qs, int qe)
{
// Check for erroneous input values
if (qs < 0 || qe > n-1 || qs > qe)
{
printf("Invalid Input");
return -1;
}
return getXorUtil(st, 0, n-1, qs, qe, 0);
}
// A 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)
{
// If there is one element in array, store it in current node of
// segment tree and return
if (ss == se)
{
st[si] = arr[ss];
return arr[ss];
}
// If there are more than one elements, then recur for left and
// right subtrees and store the xor of values in this node
int mid = getMid(ss, se);
st[si] = constructSTUtil(arr, ss, mid, st, si*2+1) ^
constructSTUtil(arr, mid+1, se, st, si*2+2);
return st[si];
}
/* Function to construct 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)
{
// 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;
// Allocate memory
int *st = (int *)malloc(sizeof(int)*max_size);
// Fill the allocated memory st
constructSTUtil(arr, 0, n-1, st, 0);
// Return the constructed segment tree
return st;
}
// Driver program to test above functions
int main()
{
int arr[] = {1, 3, 5, 7, 9, 11};
int n = sizeof(arr)/sizeof(arr[0]);
// Build segment tree from given array
int *st = constructST(arr, n);
// Print xor of values in array from index 1 to 3
printf("Xor of values in given range = %d\n",
getXor(st, n, 1, 3));
// Update: set arr[1] = 10 and update corresponding
// segment tree nodes
updateValue(arr, st, n, 1, 10);
// Find xor after the value is updated
printf("Updated xor of values in given range = %d\n",
getXor(st, n, 1, 3));
return 0;
}
Java
// Java program to show segment tree operations
// like construction, query and update
class GFG{
// A utility function to get the middle
// index from corner indexes.
static int getMid(int s, int e)
{
return s + (e - s) / 2;
}
/*
* A recursive function to get the xor of values
* in given range of the array.
* The following are parameters for this function.
*
* st --> Pointer to segment tree
* si --> 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[si]
* qs & qe --> Starting and ending indexes of query range
*/
static int getXorUtil(int[] st, int ss, int se,
int qs, int qe, int si)
{
// If segment of this node is a part of
// given range, then return the xor of
// the segment
if (qs <= ss && qe >= se)
return st[si];
// 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 getXorUtil(st, ss, mid, qs,
qe, 2 * si + 1) ^
getXorUtil(st, mid + 1, se, qs,
qe, 2 * si + 2);
}
/*
* A recursive function to update the nodes which have the given
* index in their range. The following are parameters
* st, si, ss and se are same as getXorUtil()
* 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);
}
}
// The function to update a value in 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)
{
// Check for erroneous input index
if (i < 0 || i > n - 1)
{
System.out.println("Invalid Input");
return;
}
// Get the difference between new
// value and old value
int diff = new_val - arr[i];
// Update the value in array
arr[i] = new_val;
// Update the values of nodes in segment tree
updateValueUtil(st, 0, n - 1, i, diff, 0);
}
// Return xor of elements in range from
// index qs (quey start) to qe (query end).
// It mainly uses getXorUtil()
static int getXor(int[] st, int n, int qs, int qe)
{
// Check for erroneous input values
if (qs < 0 || qe > n - 1 || qs > qe)
{
System.out.println("Invalid Input");
return -1;
}
return getXorUtil(st, 0, n - 1, qs, qe, 0);
}
// A 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)
{
// If there is one element in array, store
// it in current node of segment tree and return
if (ss == se)
{
st[si] = arr[ss];
return arr[ss];
}
// If there are more than one elements,
// then recur for left and right subtrees
// and store the xor of values in this node
int mid = getMid(ss, se);
st[si] = constructSTUtil(arr, ss, mid, st,
si * 2 + 1) ^
constructSTUtil(arr, mid + 1, se, st,
si * 2 + 2);
return st[si];
}
/*
* Function to construct 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)
{
// 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;
// Allocate memory
int[] st = new int[max_size];
// Fill the allocated memory st
constructSTUtil(arr, 0, n - 1, st, 0);
// Return the constructed segment tree
return st;
}
// Driver code
public static void main(String[] args)
{
int[] arr = { 1, 3, 5, 7, 9, 11 };
int n = arr.length;
// Build segment tree from given array
int[] st = constructST(arr, n);
// Print xor of values in array from index 1 to 3
System.out.printf("Xor of values in given " +
"range = %d\n",
getXor(st, n, 1, 3));
// Update: set arr[1] = 10 and update
// corresponding segment tree nodes
updateValue(arr, st, n, 1, 10);
// Find xor after the value is updated
System.out.printf("Updated xor of values in " +
"given range = %d\n",
getXor(st, n, 1, 3));
}
}
// This code is contributed by sanjeev2552
输出:
Xor of values in given range = 1
Updated xor of values in given range = 8
时空复杂性:
树构建的时间复杂度为O(n)。总共有2n-1个节点,并且在树结构中每个节点的值仅计算一次。
查询的时间复杂度为O(log n)。
更新的时间复杂度也是O(log n)。
总时间复杂度为:构造的O(n)+每个查询的O(log n)= O(n)+ O(n * log n)= O(n * log n)
Time Complexity O(n * log n)
Auxiliary Space O(n)