【问题标题】:How to get the minimum XOR of a given value and the value from a query of range for a given array如何从给定数组的范围查询中获取给定值的最小异或值
【发布时间】:2021-10-17 04:11:12
【问题描述】:

给定一个包含 n 个整数的数组 A,并以范围 [l , r] 和值 x 的形式给出查询,找到 A[i] XOR x 的最小值,其中 l

我尝试使用段树来解决这个问题,但我不确定我应该在其中存储什么类型的信息,因为 x 对于不同的查询会有所不同。

0 < number of queries <= 1e4

0 < n <= 1e4 

【问题讨论】:

  • 请提供对nx的限制

标签: c++ algorithm xor trie segment-tree


【解决方案1】:

为了解决这个问题,我使用 std::vector 作为基础(不是数组或 std::array),只是为了灵活性。

#include <algorithm>
#include <stdexcept>
#include <vector>

int get_xored_max(const std::vector<int>& values, const size_t l, const size_t r, const int xor_value)
{
    // check bounds of l and r
    if ((l >= values.size()) || (r >= values.size()))
    {
        throw std::invalid_argument("index out of bounds");
    }

    // todo check l < r

    // create left & right iterators to create a smaller vector 
    // only containing the subset we're interested in.
    auto left = values.begin() + l;
    auto right = values.begin() + r + 1;
    std::vector<int> range{ left, right };

    // xor all the values in the subset
    for (auto& v : range)
    {
        v ^= xor_value;
    }

    // use the standard library function for finding the iterator to the maximum
    // then use the * to dereference the iterator and get the value
    auto max_value = *std::max_element(range.begin(), range.end());
    return max_value;
}

int main()
{
    std::vector<int> values{ 1,3,5,4,2,4,7,9 };
    auto max_value = get_xored_max(values, 0u, 7u, 3);
    return 0;
}

【讨论】:

  • 我认为 OP 想要一个更有效的解决方案,而不是天真的蛮力解决方案,因为他正在考虑使用分段树。
  • 可能是:) 我在他的问题中没有看到任何性能优化要求。因此,如果性能不是硬性要求,我只是满足于尽可能多地重用标准库实现(即使这样,标准库容器也可以非常快)。但如果他的任务规定他应该使用树,那么我就更正了:)
  • 感谢您的回答,但正如@LearningMathematics 所说,我想要一个有效的解决方案,很抱歉我没有指定约束。我将进行编辑。
  • 哦,但这可能意味着在给出新的 x(xor 值)时重新填充树。这将是 O(n)。 (再次对树进行排序可能是 O(n log n))那么什么应该更有效呢?更改 x,或搜索子范围 [l,r]。最后,您可能会为过去看到的每个 x 创建一棵树,并为每个新 x 构建一棵新树。然后你可以在你的树中有效地搜索 [l,r] 范围
【解决方案2】:

方法 - Trie + 离线处理
时间复杂度 - O(N32)
空间复杂度 - O(N
32)

编辑:
这种方法会失败。我想,我们必须使用平方根分解而不是两个指针的方法。

我已经使用 Trie 在 [l,r] 范围内找到最小 xor 解决了这个问题。我通过对查询进行排序,通过离线处理解决了查询。

输入格式:
第一行有 n(元素数量)和 q(查询数量)。第二行包含数组的所有 n 个元素。每个后续行都有一个查询,每个查询有 3 个输入 l、r 和 x。

示例 -
输入 -

3 3
2 1 2
1 2 3
1 3 2
2 3 5

首先,将所有 3 个查询转换为按 l 和 r 排序的查询。
转换后的查询 -

1 2 3
1 3 2
2 3 5

这里的关键是使用两个指针方法处理排序查询。

#include <bits/stdc++.h>
using namespace std;

const int N = (int)2e4 + 77;

int n, q, l, r, x;
int a[N], ans[N];
vector<pair<pair<int, int>, pair<int, int>>> queries;

// Trie Implementation starts
struct node
{
  int nxt[2], cnt;
  void newnode()
  {
    memset(nxt, 0, sizeof(nxt));
    cnt = 0;
  }
} trie[N * 32];
int tot = 1;

void update(int x, int v)
{
  int p = 1;
  for (int i = 31; i >= 0; i--)
  {
    int id = x >> i & 1;
    if (!trie[p].nxt[id])
    {
      trie[++tot].newnode();
      trie[p].nxt[id] = tot;
    }
    p = trie[p].nxt[id];
    trie[p].cnt += v;
  }
}

int minXor(int x)
{
  int res = 0, p = 1;
  for (int i = 31; i >= 0; i--)
  {
    int id = x >> i & 1;
    if (trie[p].nxt[id] and trie[trie[p].nxt[id]].cnt)
      p = trie[p].nxt[id];
    else
    {
      p = trie[p].nxt[id ^ 1];
      res |= 1 << i;
    }
  }
  return res;
}
// Trie Implementation ends

int main()
{
  cin >> n >> q;
  for (int i = 1; i <= n; i += 1)
  {
    cin >> a[i];
  }
  for (int i = 1; i <= q; i += 1)
  {
    cin >> l >> r >> x;
    queries.push_back({{l, r}, {x, i}});
  }
  sort(queries.begin(), queries.end());
  int left = 1, right = 1;
  for (int i = 0; i < q; i += 1)
  {
    int l = queries[i].first.first;
    int r = queries[i].first.second;
    int x = queries[i].second.first;
    int index = queries[i].second.second;
    while (left < l)
    {
      update(a[left], -1);
      left += 1;
    }
    while (right <= r)
    {
      update(a[right], 1);
      right += 1;
    }
    ans[index] = minXor(x);
  }
  for (int i = 1; i <= q; i += 1)
  {
    cout << ans[i] << " \n";
  }
  return 0;
}

【讨论】:

  • 如果你将这个想法与OP 使用分段树并为每个分段构建 trie 的想法。
【解决方案3】:

编辑:使用 O(位数)代码

使用二叉树存储A的值,看这里:Minimum XOR for queries

您需要更改的是向每个节点添加与叶子中的值相对应的 A 的索引范围。

# minimal xor in a range
nbits=16    # Number of bits for numbers
asize=5000 # Array size
ntest=50     # Number of random test
from random import randrange

# Insert element a iindex iin the tree (increasing i only)
def tinsert(a,i,T):
for b in range(nbits-1,-1,-1):
    v=((a>>b)&1)
    T[v+2].append(i)
    if T[v]==[]:T[v]=[[],[],[],[]]
    T=T[v]
    
# Buildtree : builds a tree based on array V
def build(V):
T=[[],[],[],[]] # Init tree
for i,a in enumerate(V): tinsert(a,i,T)
return(T)

# Binary search : is T intersec [a,b] non empty ?
def binfind(T,a,b):
 s,e,om=0,len(T)-1,-1
 while True:
  m=(s+e)>>1
  v=T[m]
  if v<a: 
   s=m
   if m==om: return(a<=T[e]<=b)
  elif v>b: 
   e=m
   if m==om: return(a<=T[s]<=b)
  else: return(True) # a<=T(m)<=b
  om=m

# Look for the min xor in a give range index
def minx(x,s,e,T):
 if s<0 or s>=(len(T[2])+len(T[3])) or e<s: return
 r=0
 for b in range(nbits-1,-1,-1):
  v=((x>>b)&1)
  if T[v+2]==[] or not binfind(T[v+2],s,e): # not nr with b set to v ?
   v=1-v
  T=T[v]
   r=(r<<1)|v
 return(r)

# Tests the code on random arrays
max=(1<<nbits)-1
for i in range(ntest):
 A=[randrange(0,max) for i in range(asize)]
 T=build(A)
 x,s=randrange(0,max),randrange(0,asize-1)
 e=randrange(s,asize)
 if min(v^x for v in A[s:e+1])!=x^minx(x,s,e,T):
  print('error')

【讨论】:

    【解决方案4】:

    我能够使用分段树解决这个问题,并按照@David Eisenstat 的建议进行尝试

    以下是 C++ 的实现。 我为段树中的每个段构建了一个 trie。而找到最小异或只是使用查询值的每一位(here)遍历和匹配对应的trie

    #include <bits/stdc++.h>
    #define rep(i, a, b) for (int i = a; i < b; i++)
    using namespace std;
    
    const int bits = 7;
    
    struct trie {
        trie *children[2];
        bool end;
    };
    
    trie *getNode(void)
    {
        trie *node        = new trie();
        node->end         = false;
        node->children[0] = NULL;
        node->children[1] = NULL;
        return node;
    }
    
    trie *merge(trie *l, trie *r)
    {
        trie *node = getNode();
        // Binary 0:
        if (l->children[0] && r->children[0])
            node->children[0] = merge(l->children[0], r->children[0]);
    
        else if (!r->children[0])
            node->children[0] = l->children[0];
    
        else if (!l->children[0])
            node->children[0] = r->children[0];
    
        // Binary 1:
        if (l->children[1] && r->children[1])
            node->children[1] = merge(l->children[1], r->children[1]);
    
        else if (!r->children[1])
            node->children[1] = l->children[1];
    
        else if (!l->children[1])
            node->children[1] = r->children[1];
    
        return node;
    }
    
    void insert(trie *root, int num)
    {
        int mask = 1 << bits;
        int bin;
        rep(i, 0, bits + 1)
        {
            bin = ((num & mask) >> (bits - i));
            if (!root->children[bin]) root->children[bin] = getNode();
            root = root->children[bin];
            mask = mask >> 1;
        }
    
        root->end = true;
    }
    
    struct _segTree {
        int n, height, size;
        vector<trie *> tree;
    
        _segTree(int _n)
        {
            n      = _n;
            height = (int)ceil(log2(n));
            size   = (int)(2 * pow(2, height) - 1);
            tree.resize(size);
        }
    
        trie *construct(vector<int> A, int start, int end, int idx)
        {
            if (start == end) {
                tree[idx] = getNode();
                insert(tree[idx], A[start]);
                return tree[idx];
            }
    
            int mid   = start + (end - start) / 2;
            tree[idx] = merge(construct(A, start, mid, 2 * idx + 1),
                              construct(A, mid + 1, end, 2 * idx + 2));
            return tree[idx];
        }
    
        int findMin(int num, trie *root)
        {
            int mask = 1 << bits;
            int bin;
    
            int rnum = 0;
            int res  = 0;
    
            rep(i, 0, bits + 1)
            {
                bin = ((num & mask) >> (bits - i));
    
                if (!root->children[bin]) {
                    bin = 1 - bin;
                    if (!root->children[bin]) return res ^ num;
                }
    
                rnum |= (bin << (bits - i));
                root = root->children[bin];
                if (root->end) res = rnum;
                mask = mask >> 1;
            }
            return res ^ num;
        }
    
        int Query(int X, int start, int end, int qstart, int qend, int idx)
        {
            if (qstart <= start && qend >= end) return findMin(X, tree[idx]);
    
            if (qstart > end || qend < start) return INT_MAX;
    
            int mid = start + (end - start) / 2;
            return min(Query(X, start, mid, qstart, qend, 2 * idx + 1),
                       Query(X, mid + 1, end, qstart, qend, 2 * idx + 2));
        }
    };
    
    int main()
    {
        int n, q;
        vector<int> A;
        vector<int> L;
        vector<int> R;
        vector<int> X;
    
        cin >> n;
        A.resize(n, 0);
    
        rep(i, 0, n) cin >> A[i];
    
        cin >> q;
        L.resize(q);
        R.resize(q);
        X.resize(q);
        rep(i, 0, q) cin >> L[i] >> R[i] >> X[i];
    
        //---------------------code--------------------//
    
        _segTree segTree(n);
        segTree.construct(A, 0, n - 1, 0);
    
        rep(i, 0, q)
        {
            cout << segTree.Query(X[i], 0, n - 1, L[i], R[i], 0) << " ";
        }
    
        return 0;
    }
    

    时间复杂度:O((2n - 1)*k + qklogn)

    空间复杂度:O((2n - 1)*2k)

    k -> 位数

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-12
      • 2020-10-16
      • 2021-11-16
      • 1970-01-01
      相关资源
      最近更新 更多