📌  相关文章
📜  动态细分树:具有点更新的范围总和的在线查询

📅  最后修改于: 2021-04-17 13:29:12             🧑  作者: 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 =(上一个答案+ L)%N +1
  • R =(上一个答案+ 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]。然后,其左和右子树的间隔分别为[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