📌  相关文章
📜  动态段树:带点更新的范围和的在线查询

📅  最后修改于: 2021-09-03 03:27:11             🧑  作者: Mango

先决条件:段树
给定一个数字N ,它表示初始化为 0 的数组的大小和Q查询,其中有两种类型的查询:

  1. 1 PV:将值V放在位置P
  2. 2 LR:输出从LR的值的总和。

任务是回答这些查询。
约束:

  • 1 ≤ N ≤ 10 18
  • Q≤10 5
  • 1≤L≤R≤N

注:在线查询。所以:

  • L = (previousAnswer + L) % N + 1
  • R = (previousAnswer + R) % N + 1

例子:

方法:在这里,由于更新量很高,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()函数被完全删除。

在进入操作算法之前,让我们定义本文中使用的术语:

  • 节点的区间:它是节点所代表的区间。
  • 所需时间间隔:要计算总和的时间间隔。
  • 必需索引:需要更新的索引。

以下是用于对具有上述属性的树进行操作的算法:

  1. 点更新:以下算法用于点更新:
    1. 从根节点开始。
    2. 如果节点的间隔与所需的索引不重叠,则返回。
    3. 如果节点是 NULL 条目,则创建一个具有适当间隔的新节点,并通过为每个新创建的子节点返回到步骤 2 来下降到该节点。
    4. 如果间隔和要存储值的索引相等,则将值存储到该节点。
    5. 如果节点上的区间与所需索引部分重叠,则下降到其子节点并从步骤 2 继续执行。
  2. 查找每个查询的总和:以下算法用于查找每个查询的总和:
    1. 从根节点开始。
    2. 如果节点为 NULL 或该节点处的间隔与所需间隔不重叠,则返回 0。
    3. 如果节点上的间隔与所需的间隔完全重叠,则返回存储在节点上的值。
    4. 如果节点上的间隔与所需的间隔部分重叠,则下降到其子节点并继续从步骤 2 对其两个子节点执行。

示例:让我们通过示例来可视化更新和求和。设 N = 10,需要对树执行的操作如下:

  1. 在位置 1 插入 10。
  2. 求从 2 到 8 的索引值的总和。
  3. 在位置 5 插入 3。
  4. 求从 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