Code. Implementing Fenwick trees with bit tricks in Java [blog/fenwick/java_fenwick]

class FenwickTree {
    private long[] a;
    public FenwickTree(int n) { a = new long[n+1]; }
    public long prefix(int i) {
        long s = 0;
        for (; i > 0; i -= LSB(i)) s += a[i]; return s;
    }
    public void update(int i, long delta) {
        for (; i < a.length; i += LSB(i)) a[i] += delta;
    }
    public long range(int i, int j) {
        return prefix(j) - prefix(i-1);
    }
    public long get(int i) { return range(i,i); }
    public void set(int i, long v) { update(i, v - get(i)); }
    private int LSB(int i) { return i & (-i); }
}

本节展示了一个典型的 Java 语言芬威克树实现。可见,其实现异常简洁,大体由若干小型循环构成,每个循环迭代仅执行少量的算术与位运算。然而,这段代码究竟在做什么、其工作原理为何,却不甚明了!稍加审视,rangegetset 函数尚属直观,但其余函数则令人困惑。我们可以观察到,prefixupdate 函数均调用了另一个名为 LSB 的函数,该函数不知何故对一个整数及其负数执行了按位逻辑与运算。事实上,LSB(x) 计算的是 $x$ 的最低有效位(least significant bit),即它返回最小的 $2^k$,使得 $x$ 的第 $k$ 位为 1。然而,LSB 的实现原理,以及最低有效位在此处用于计算更新和前缀和的方式与缘由,均非显而易见。