先决条件:段树
给定一个数字N ,它表示初始化为 0 的数组的大小和Q查询,其中有两种类型的查询:
- 1 PV:将值V放在位置P 。
- 2 LR:输出从L到R的值的总和。
任务是回答这些查询。
约束:
- 1 ≤ N ≤ 10 18
- Q≤10 5
- 1≤L≤R≤N
注:在线查询。所以:
- L = (previousAnswer + L) % N + 1
- R = (previousAnswer + 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]。那么,它的左右子树的Interval分别是[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
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live