先决条件:段树
给定一个数字N ,该数字表示初始化为0的数组的大小,并进行Q个查询以处理存在两种类型的查询的情况:
- 1 PV:将值V置于位置P。
- 2 LR:从L到R输出值的总和。
任务是回答这些查询。
限制条件:
- 1≤N≤10 18
- Q≤10 5
- 1≤L≤R≤N
注意:查询在线。所以:
- L =(上一个答案+ L)%N +1
- R =(上一个答案+ R)%N +1
例子:
Input: N = 5, Q = 5, arr[][] = {{1, 2, 3}, {1, 1, 4}, {1, 3, 5}, {1, 4, 7}, {2, 3, 4}}
Output: 12
Explanation:
There are five queries. Since N = 5, therefore, initially, the array is {0, 0, 0, 0, 0}
For query 1: 1 2 3 array = {0, 3, 0, 0, 0}
For query 2: 1 1 4 array = {4, 3, 0, 0, 0}
For query 3: 1 3 5 array = {4, 3, 5, 0, 0}
For query 4: 1 4 7 array = {4, 3, 5, 7, 0}
For query 5: 2 3 4 Sum from [3, 4] = 7 + 5 = 12.
Input: N = 3, Q = 2, arr[][] = {{1, 1, 1}, {1, 2, 2}, {1, 3, 3}}
Output: 0
方法:在这里,由于更新次数很高,因此Kadane的算法效果不佳。此外,由于假定查询是在线的,所以简单的段树将无法解决此问题,因为对元素数量的约束非常高。因此,在此问题中使用一种新型的数据结构,即动态段树。
动态细分树:动态细分树不是新的数据结构。它与段树非常相似。以下是动态细分树的属性:
- 每当要更新新的间隔时,都会创建一个节点,而不是使用数组来表示间隔。
- 以下是动态段树的节点的结构:
C++
// Every node contains the value and
// the left subtree and right subtree
struct Node {
long long value;
struct Node *L, *R;
};
struct Node* getnode()
{
struct Node* temp = new struct Node;
temp->value = 0;
temp->L = NULL;
temp->R = NULL;
return temp;
}
CPP
// C++ program for the implementation
// of the Dynamic segment tree and
// perform the range updates on the
// given queries
#include
using namespace std;
typedef long long ll;
// Structure of the node
struct Node {
ll value;
struct Node *L, *R;
};
// Structure to get the newly formed
// node
struct Node* getnode()
{
struct Node* temp = new struct Node;
temp->value = 0;
temp->L = NULL;
temp->R = NULL;
return temp;
}
// Creating the Root node
struct Node* root;
// Function to perform the point update
// on the dynamic segment tree
void UpdateHelper(struct Node* curr, ll index,
ll L, ll R, ll val)
{
// If the index is not overlapping
// with the index
if (L > index || R < index)
return;
// If the index is completely overlapping
// with the index
if (L == R && L == index) {
// Update the value of the node
// to the given value
curr->value = val;
return;
}
// Computing the middle index if none
// of the above base cases are satisfied
ll mid = L - (L - R) / 2;
ll sum1 = 0, sum2 = 0;
// If the index is in the left subtree
if (index <= mid) {
// Create a new node if the left
// subtree is is null
if (curr->L == NULL)
curr->L = getnode();
// Recursively call the function
// for the left subtree
UpdateHelper(curr->L, index, L, mid, val);
}
// If the index is in the right subtree
else {
// Create a new node if the right
// subtree is is null
if (curr->R == NULL)
curr->R = getnode();
// Recursively call the function
// for the right subtree
UpdateHelper(curr->R, index, mid + 1, R, val);
}
// Storing the sum of the left subtree
if (curr->L)
sum1 = curr->L->value;
// Storing the sum of the right subtree
if (curr->R)
sum2 = curr->R->value;
// Storing the sum of the children into
// the node's value
curr->value = sum1 + sum2;
return;
}
// Function to find the sum of the
// values given by the range
ll queryHelper(struct Node* curr, ll a,
ll b, ll L, ll R)
{
// Return 0 if the root is null
if (curr == NULL)
return 0;
// If the index is not overlapping
// with the index, then the node
// is not created. So sum is 0
if (L > b || R < a)
return 0;
// If the index is completely overlapping
// with the index, return the node's value
if (L >= a && R <= b)
return curr->value;
ll mid = L - (L - R) / 2;
// Return the sum of values stored
// at the node's children
return queryHelper(curr->L, a, b, L, mid)
+ queryHelper(curr->R, a, b, mid + 1, R);
}
// Function to call the queryHelper
// function to find the sum for
// the query
ll query(int L, int R)
{
return queryHelper(root, L, R, 1, 10);
}
// Function to call the UpdateHelper
// function for the point update
void update(int index, int value)
{
UpdateHelper(root, index, 1, 10, value);
}
// Function to perform the operations
// on the tree
void operations()
{
// Creating an empty tree
root = getnode();
// Update the value at position 1 to 10
update(1, 10);
// Update the value at position 3 to 5
update(3, 5);
// Finding sum for the range [2, 8]
cout << query(2, 8) << endl;
// Finding sum for the range [1, 10]
cout << query(1, 10) << endl;
}
// Driver code
int main()
{
operations();
return 0;
}
Java
// Java program for the implementation
// of the Dynamic segment tree and
// perform the range updates on the
// given queries
class GFG {
// Structure of the node
static class Node {
int value;
Node L, R;
}
// Structure to get the newly formed
// node
static Node getnode() {
Node temp = new Node();
temp.value = 0;
temp.L = null;
temp.R = null;
return temp;
}
// Creating the Root node
static Node root = new Node();
// Function to perform the point update
// on the dynamic segment tree
static void UpdateHelper(Node curr, int index, int L, int R, int val) {
// If the index is not overlapping
// with the index
if (L > index || R < index)
return;
// If the index is completely overlapping
// with the index
if (L == R && L == index) {
// Update the value of the node
// to the given value
curr.value = val;
return;
}
// Computing the middle index if none
// of the above base cases are satisfied
int mid = L - (L - R) / 2;
int sum1 = 0, sum2 = 0;
// If the index is in the left subtree
if (index <= mid) {
// Create a new node if the left
// subtree is is null
if (curr.L == null)
curr.L = getnode();
// Recursively call the function
// for the left subtree
UpdateHelper(curr.L, index, L, mid, val);
}
// If the index is in the right subtree
else {
// Create a new node if the right
// subtree is is null
if (curr.R == null)
curr.R = getnode();
// Recursively call the function
// for the right subtree
UpdateHelper(curr.R, index, mid + 1, R, val);
}
// Storing the sum of the left subtree
if (curr.L != null)
sum1 = curr.L.value;
// Storing the sum of the right subtree
if (curr.R != null)
sum2 = curr.R.value;
// Storing the sum of the children into
// the node's value
curr.value = sum1 + sum2;
return;
}
// Function to find the sum of the
// values given by the range
static int queryHelper(Node curr, int a, int b, int L, int R) {
// Return 0 if the root is null
if (curr == null)
return 0;
// If the index is not overlapping
// with the index, then the node
// is not created. So sum is 0
if (L > b || R < a)
return 0;
// If the index is completely overlapping
// with the index, return the node's value
if (L >= a && R <= b)
return curr.value;
int mid = L - (L - R) / 2;
// Return the sum of values stored
// at the node's children
return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1, R);
}
// Function to call the queryHelper
// function to find the sum for
// the query
static int query(int L, int R) {
return queryHelper(root, L, R, 1, 10);
}
// Function to call the UpdateHelper
// function for the point update
static void update(int index, int value) {
UpdateHelper(root, index, 1, 10, value);
}
// Function to perform the operations
// on the tree
static void operations() {
// Creating an empty tree
root = getnode();
// Update the value at position 1 to 10
update(1, 10);
// Update the value at position 3 to 5
update(3, 5);
// Finding sum for the range [2, 8]
System.out.println(query(2, 8));
// Finding sum for the range [1, 10]
System.out.println(query(1, 10));
}
// Driver code
public static void main(String[] args) {
operations();
}
}
// This code is contributed by sanjeev2552
- 显然,上述结构与二进制搜索树相同。在每个节点中,我们都存储该节点的值和两个指向左侧和右侧子树的指针。
- 根的间隔为[1,N],左子树的间隔为[1,N / 2],右子树的间隔为[N / 2 + 1,N]。
- 同样,对于每个节点,我们都可以计算其表示的间隔。假设当前节点的间隔为[L,R]。然后,其左和右子树的间隔分别为[L,(L + R)/ 2]和[(L + R)/ 2 + 1,R]。
- 由于我们仅在需要时才创建新节点,因此段树中的build()函数被完全删除。
在开始运算算法之前,让我们定义一下本文中使用的术语:
- 节点间隔:这是节点代表的间隔。
- 所需间隔:要计算总和的间隔。
- 必需的索引:需要更新的索引。
以下是用于对具有上述属性的树进行操作的算法:
- 点更新:以下算法用于点更新:
- 从根节点开始。
- 如果节点的间隔不与所需索引重叠,则返回。
- 如果该节点是NULL条目,则以适当的间隔创建一个新节点,并通过对每个创建的新子节点返回步骤2来进入该节点。
- 如果间隔和要存储值的索引均相等,则将值存储到该节点上。
- 如果节点处的间隔与所需索引部分重叠,则下降到其子节点并继续从步骤2开始执行。
- 查找每个查询的总和:以下算法用于查找每个查询的总和:
- 从根节点开始。
- 如果该节点为NULL或该节点的间隔不与所需间隔重叠,则返回0。
- 如果节点上的间隔与所需间隔完全重叠,则返回存储在节点上的值。
- 如果节点上的间隔与所需间隔部分重叠,则下降到其子级,并继续从步骤2开始对其两个子级执行。
示例:让我们以示例的形式显示更新和总和。令N = 10,并且在树上执行所需的操作如下:
- 在位置1插入10。
- 查找2到8之间的索引值总和。
- 在位置5插入3。
- 找出3到6之间的索引值总和。
- 最初,对于值N = 10,树为空。所以:
- 在位置1插入10。为此,创建一个新节点,直到获得所需的间隔。所以:
- 找到2到8的索引值的总和。为此,要找到[1,8]的总和,然后从中减去[1,2]的值。由于尚未创建节点[1,8],因此[1,8]的值为根[1,10]的值。所以:
- 在位置5插入3。为此,创建一个新节点,直到获得所需的间隔。所以:
下面是上述方法的实现:
CPP
// C++ program for the implementation
// of the Dynamic segment tree and
// perform the range updates on the
// given queries
#include
using namespace std;
typedef long long ll;
// Structure of the node
struct Node {
ll value;
struct Node *L, *R;
};
// Structure to get the newly formed
// node
struct Node* getnode()
{
struct Node* temp = new struct Node;
temp->value = 0;
temp->L = NULL;
temp->R = NULL;
return temp;
}
// Creating the Root node
struct Node* root;
// Function to perform the point update
// on the dynamic segment tree
void UpdateHelper(struct Node* curr, ll index,
ll L, ll R, ll val)
{
// If the index is not overlapping
// with the index
if (L > index || R < index)
return;
// If the index is completely overlapping
// with the index
if (L == R && L == index) {
// Update the value of the node
// to the given value
curr->value = val;
return;
}
// Computing the middle index if none
// of the above base cases are satisfied
ll mid = L - (L - R) / 2;
ll sum1 = 0, sum2 = 0;
// If the index is in the left subtree
if (index <= mid) {
// Create a new node if the left
// subtree is is null
if (curr->L == NULL)
curr->L = getnode();
// Recursively call the function
// for the left subtree
UpdateHelper(curr->L, index, L, mid, val);
}
// If the index is in the right subtree
else {
// Create a new node if the right
// subtree is is null
if (curr->R == NULL)
curr->R = getnode();
// Recursively call the function
// for the right subtree
UpdateHelper(curr->R, index, mid + 1, R, val);
}
// Storing the sum of the left subtree
if (curr->L)
sum1 = curr->L->value;
// Storing the sum of the right subtree
if (curr->R)
sum2 = curr->R->value;
// Storing the sum of the children into
// the node's value
curr->value = sum1 + sum2;
return;
}
// Function to find the sum of the
// values given by the range
ll queryHelper(struct Node* curr, ll a,
ll b, ll L, ll R)
{
// Return 0 if the root is null
if (curr == NULL)
return 0;
// If the index is not overlapping
// with the index, then the node
// is not created. So sum is 0
if (L > b || R < a)
return 0;
// If the index is completely overlapping
// with the index, return the node's value
if (L >= a && R <= b)
return curr->value;
ll mid = L - (L - R) / 2;
// Return the sum of values stored
// at the node's children
return queryHelper(curr->L, a, b, L, mid)
+ queryHelper(curr->R, a, b, mid + 1, R);
}
// Function to call the queryHelper
// function to find the sum for
// the query
ll query(int L, int R)
{
return queryHelper(root, L, R, 1, 10);
}
// Function to call the UpdateHelper
// function for the point update
void update(int index, int value)
{
UpdateHelper(root, index, 1, 10, value);
}
// Function to perform the operations
// on the tree
void operations()
{
// Creating an empty tree
root = getnode();
// Update the value at position 1 to 10
update(1, 10);
// Update the value at position 3 to 5
update(3, 5);
// Finding sum for the range [2, 8]
cout << query(2, 8) << endl;
// Finding sum for the range [1, 10]
cout << query(1, 10) << endl;
}
// Driver code
int main()
{
operations();
return 0;
}
Java
// Java program for the implementation
// of the Dynamic segment tree and
// perform the range updates on the
// given queries
class GFG {
// Structure of the node
static class Node {
int value;
Node L, R;
}
// Structure to get the newly formed
// node
static Node getnode() {
Node temp = new Node();
temp.value = 0;
temp.L = null;
temp.R = null;
return temp;
}
// Creating the Root node
static Node root = new Node();
// Function to perform the point update
// on the dynamic segment tree
static void UpdateHelper(Node curr, int index, int L, int R, int val) {
// If the index is not overlapping
// with the index
if (L > index || R < index)
return;
// If the index is completely overlapping
// with the index
if (L == R && L == index) {
// Update the value of the node
// to the given value
curr.value = val;
return;
}
// Computing the middle index if none
// of the above base cases are satisfied
int mid = L - (L - R) / 2;
int sum1 = 0, sum2 = 0;
// If the index is in the left subtree
if (index <= mid) {
// Create a new node if the left
// subtree is is null
if (curr.L == null)
curr.L = getnode();
// Recursively call the function
// for the left subtree
UpdateHelper(curr.L, index, L, mid, val);
}
// If the index is in the right subtree
else {
// Create a new node if the right
// subtree is is null
if (curr.R == null)
curr.R = getnode();
// Recursively call the function
// for the right subtree
UpdateHelper(curr.R, index, mid + 1, R, val);
}
// Storing the sum of the left subtree
if (curr.L != null)
sum1 = curr.L.value;
// Storing the sum of the right subtree
if (curr.R != null)
sum2 = curr.R.value;
// Storing the sum of the children into
// the node's value
curr.value = sum1 + sum2;
return;
}
// Function to find the sum of the
// values given by the range
static int queryHelper(Node curr, int a, int b, int L, int R) {
// Return 0 if the root is null
if (curr == null)
return 0;
// If the index is not overlapping
// with the index, then the node
// is not created. So sum is 0
if (L > b || R < a)
return 0;
// If the index is completely overlapping
// with the index, return the node's value
if (L >= a && R <= b)
return curr.value;
int mid = L - (L - R) / 2;
// Return the sum of values stored
// at the node's children
return queryHelper(curr.L, a, b, L, mid) + queryHelper(curr.R, a, b, mid + 1, R);
}
// Function to call the queryHelper
// function to find the sum for
// the query
static int query(int L, int R) {
return queryHelper(root, L, R, 1, 10);
}
// Function to call the UpdateHelper
// function for the point update
static void update(int index, int value) {
UpdateHelper(root, index, 1, 10, value);
}
// Function to perform the operations
// on the tree
static void operations() {
// Creating an empty tree
root = getnode();
// Update the value at position 1 to 10
update(1, 10);
// Update the value at position 3 to 5
update(3, 5);
// Finding sum for the range [2, 8]
System.out.println(query(2, 8));
// Finding sum for the range [1, 10]
System.out.println(query(1, 10));
}
// Driver code
public static void main(String[] args) {
operations();
}
}
// This code is contributed by sanjeev2552
5
15