📌  相关文章
📜  使用Fenwick树对给定范围内的元素进行XOR与更新(1)

📅  最后修改于: 2023-12-03 15:22:18.446000             🧑  作者: Mango

使用Fenwick树对给定范围内的元素进行XOR与更新

简介

Fenwick树,又叫二叉索引树或者树状数组,是一种支持动态单点修改和查询前缀和的数据结构。常常用于动态维护区间和。

本文将介绍如何使用Fenwick树对给定范围内的元素进行XOR与更新,同时介绍相关算法的实现与时间复杂度。

实现

对于一个序列A,首先我们需要对序列A构建Fenwick树。具体而言,令F为对应的Fenwick树,则对于每个位置i,有:

$F_i=\bigoplus_{j=i-lowbit(i)+1}^i {A_j}$

其中lowbit(i)表示i的最后一个二进制位1所对应的值。

然后,我们可以利用Fenwick树支持的单点修改功能进行XOR操作。具体而言,令[l,r]表示需要进行XOR更新的区间,我们可以对Fenwick树进行如下操作:

$\Delta_F(l,x),\Delta_F(r+1,x)$

其中$\Delta_F(i,x)$表示将F[i]异或x。更新之后,我们就可以对[l,r]区间内的元素进行XOR更新。

对于查询操作,即查询[l,r]区间内所有元素的XOR值,可以进行如下计算:

$answer=\bigoplus_{i=l}^r{A_i}=\bigoplus_{i=1}^r {A_i} \bigoplus_{i=1}^{l-1} {A_i}$

由于Fenwick树可以支持前缀异或和操作,因此我们可以考虑分别对Fenwick树的F[r]与F[l-1]进行前缀异或和操作,然后将两者相异或即可得到区间异或和。

示例代码
//构建Fenwick树
vector<int> buildFenwickTree(vector<int>& A) {
    vector<int> F(A.size()+1,0);
    for(int i=1;i<=A.size();i++) {
        int j=i;
        while(j<=A.size()) {
            F[j]^=A[i-1];
            j+=j&-j;
        }
    }
    return F;
}

//单点修改
void updateFenwickTree(vector<int>& F, int index, int val) {
    for(int i=index;i<F.size();i+=(i&-i)) {
        F[i]^=val;
    }
}

//区间查询
int queryFenwickTree(vector<int>& F, int index) {
    int res=0;
    for(int i=index;i>0;i-=(i&-i)) {
        res^=F[i];
    }
    return res;
}

//查询[l,r]区间的异或和
int queryFenwickTreeRange(vector<int>& F, int l, int r) {
    return queryFenwickTree(F,r)^queryFenwickTree(F,l-1);
}
时间复杂度

Fenwick树的单点修改复杂度为O(logN),查询前缀和复杂度为O(logN),因此对于n个元素的序列,使用Fenwick树进行更新和查询的时间复杂度为O(nlogN)。对于本文介绍的区间XOR操作,需要进行两次单点修改与一次异或和查询,因此该操作的时间复杂度为O(logN)。