C++ 请告诉我有效的范围Mex查询算法

C++ 请告诉我有效的范围Mex查询算法,c++,algorithm,game-theory,C++,Algorithm,Game Theory,关于这个问题我有个问题 问题: 给你一个序列a[0],a[1],…,a[N-1],以及一组范围(l[i],r[i])(0>a[i]; 对于(int i=0;i>l>>r; 设置s; 对于(int j=l;j

关于这个问题我有个问题

问题:

  • 给你一个序列
    a[0],a[1],…,a[N-1]
    ,以及一组范围
    (l[i],r[i])(0>a[i];
    对于(int i=0;i>l>>r;
    设置s;
    对于(int j=l;jcout让我们以从左到右的方式处理查询和元素,比如

    for (int i = 0; i < N; ++i) {
        // 1. Add a[i] to all internal data structures
        // 2. Calculate answers for all queries q such that r[q] == i
    }
    
    考虑到在我们的特殊情况下,对树的所有查询都是前缀和查询,
    from
    始终等于
    0
    ,因此,对子级的一个调用总是返回一个微不足道的答案(
    0
    或已计算的
    sum
    )。因此,不要执行
    O(log N)
    查询到二维树在二进制搜索算法中,我们可以实现一个特殊的搜索过程,非常类似于这个
    get
    查询。它应该首先获取左个子节点的值(因为它已经计算了
    O(1)
    ),然后检查我们要查找的节点是否在左边(此总和小于左子树中的叶数)并根据此信息向左或向右移动。此方法将进一步优化对
    O(log^2 N)
    时间的查询(因为它现在是一个树操作),从而在时间和空间上产生
    O((N+Q)log^2 N))的复杂性

    不确定此解决方案对于
    Q
    N
    10^5
    是否足够快,但它可能会进一步优化。

    以下是
    O((Q+N)log N)
    解决方案:

  • 让我们从左到右遍历数组中的所有位置,并将每个值的最后出现次数存储在段树中(段树应在每个节点中存储最小值)

  • 添加
    i
    -第个编号后,我们可以回答右边框等于
    i
    的所有查询

  • 答案是最小的值
    x
    ,这样
    last[x]
    。我们可以从根开始向下搜索段树(如果左侧子项中的最小值小于
    l
    ,我们就到那里。否则,我们就到右侧子项)

  • 就这样

    下面是一些伪代码:

    tree = new SegmentTree() // A minimum segment tree with -1 in each position
    for i = 0 .. n - 1
        tree.put(a[i], i)
        for all queries with r = i
             ans for this query = tree.findFirstSmaller(l)
    
    查找较小的函数如下所示:

    class Tree
    {
        int l, r;           // begin and end of the interval represented by this vertex
        int sum;            // already calculated sum
        int overriden;      // value of override or special constant
        Tree *left, *right; // pointers to children
    }
    // returns sum of the part of this subtree that lies between from and to
    int Tree::get(int from, int to)
    {
        if (from > r || to < l) // no intersection
        {
            return 0;
        }
        if (l <= from && to <= r) // whole subtree lies within the interval
        {
            return sum;
        }
        if (overriden != NO_OVERRIDE) // should push override to children
        {
            left->overriden = right->overriden = overriden;
            left->sum = right->sum = (r - l) / 2 * overriden;
            overriden = NO_OVERRIDE;
        }
        return left->get(from, to) + right->get(from, to); // split to 2 queries
    }
    
    int findFirstSmaller(node, value)
        if node.isLeaf()
            return node.position()
        if node.leftChild.minimum < value
            return findFirstSmaller(node.leftChild, value)
        return findFirstSmaller(node.rightChild)
    
    int findFirstSmaller(节点,值)
    if node.isLeaf()
    返回node.position()
    如果node.leftChild.minimum<值
    返回findFirstSmaller(node.leftChild,值)
    return findFirstSmaller(node.rightChild)
    

    这个解决方案很容易编码(您只需要点更新和上面显示的
    findFisrtSmaller
    函数,我确信它对于给定的约束足够快。

    在处理第一个查询之前,您知道所有查询,对吗?我的意思是,批处理算法是可以的,您不需要在线回答。Fline算法是可以的。是这样吗分段树的rt排序问题?@Arunmu sqrt分解可以,分段树也可以,如果你能为segtree求解的话。你说O(N^2)很慢……你说的是对一个范围的求值还是对所有范围的求值?对于后者,O(N^2)事实上可能没那么糟糕…希望我的答案可以理解。如果有不清楚的地方,请发表评论。行扫描算法。我今天没有时间,所以我明天会检查。@square1001是的,扫描行+二维范围树+二进制搜索或树的自定义
    int findFirstSmaller(node, value)
        if node.isLeaf()
            return node.position()
        if node.leftChild.minimum < value
            return findFirstSmaller(node.leftChild, value)
        return findFirstSmaller(node.rightChild)