树状数组练习



目录

敌兵布阵   HDU-1166
Stars   HDU-1541
Tunnel Warfare   POJ-2892
Apple Tree   POJ-3321
Mobile phones   POJ-1195
Minimum Inversion Number   HDU-1394
Ultra-QuickSort   POJ-2299
Cows   POJ-2481


还未做分割线

MooFest   POJ-1990
Ping pong   HDU-2492
Conturbatio   HDU-5480
Japan   POJ-3067
Life is a Line   HDU-3465
Magic Ball Game   HDU-4605



题目算法盘点
不理解树状数组的可以看一下这几个博客
高级树状数组——区间修改区间查询、二维树状数组
树状数组详解
夜深人静写算法(三)- 树状数组




目录

敌兵布阵   HDU-1166

一维树状数组模板(单点修改,单点查询)

题意

敌人有 N N N 个工兵营地,第 i i i 个工兵营地里开始时有 a i a_i ai个人
每行有一条命令,命令有4种形式:
(1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)
(2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);
(3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;
(4)End 表示结束,这条命令在每组数据最后出现;

做法

树状数组模板题,当然线段树也可以
属于单点更新,区间查询
区间查询即 query® - query(l-1) ,这里利用的是差分的思想

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;

int tr[maxn], n;

int lowbit(int x) {
    return x & (-x);
}

void update(int id, int val) {
    for(int i = id; i <= n; i += lowbit(i))
        tr[i] += val;
}

int query(int id) {
    int ans = 0;
    for(int i = id; i > 0; i -= lowbit(i))
        ans += tr[i];
    return ans;
}


int main() {
    int T;
    scanf("%d", &T);
    for(int i = 1; i <= T; ++i) {
        printf("Case %d:\n", i);
        int x, y;
        scanf("%d", &n);
        for(int i = 0; i <= n; ++i) tr[i] = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &x);
            update(i, x);
        }
        char s[10];
        while(~scanf("%s", s)) {
            if(s[0] == 'E') break;
            scanf("%d%d", &x, &y);
            if(s[0] == 'Q') 
                printf("%d\n", query(y) - query(x - 1));
            else if(s[0] == 'A')
                update(x, y);
            else if(s[0] == 'S') 
                update(x, -y);
        }
    }
    return 0;
}


目录

Stars   HDU-1541

一维树状数组(单点修改,单点查询), 逆序对

题意

每个星星有一个坐标 ( x i , y i ) (x_i,y_i) (xi,yi),当有另一个星星的坐标 ( x j , y j ) (x_j,y_j) (xj,yj),且 x j ≤ x i & & y j ≤ y i x_j \leq x_i \&\& y_j \leq y_i xjxi&&yjyi 时, i i i 星星的层次加 1 1 1
输入保证 y y y 是升序的(也就是强行变一维)
最后询问层次 0 → ( n − 1 ) 0 \rightarrow (n-1) 0(n1) 的各有多少颗星星

做法

树状数组模板题,当然线段树也可以
属于单点更新,单点查询
单点查询即 q u e r y ( x ) query(x) query(x),因为保证 y y y 是升序,那么后面的星星对当前的星星都不会有贡献
统计当前星星的层次
然后单点更新 update(x, 1)
要注意的的一个地方是,本题是多组数据(很迷,我觉得题目看着是说单组数据的亚子啊 _(:з」∠)_ )

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxn = 5e4 + 5;

int tr[maxn], n;
int res[maxn];

int lowbit(int x) {
    return x & (-x);
}

void update(int id, int val) {
    for(int i = id; i < maxn; i += lowbit(i))
        tr[i] += val;
}

int query(int id) {
    int ans = 0;
    for(int i = id; i > 0; i -= lowbit(i))
        ans += tr[i];
    return ans;
}


int main() {
    int x, y, ans = 0;
    while(~scanf("%d", &n)) {
        memset(tr, 0, sizeof(tr));
        memset(res, 0, sizeof(res));
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &x, &y); 
            res[query(++x)] += 1;
            update(x, 1);
        }
        for(int i = 0; i < n; ++i)
            printf("%d\n", res[i]);
    }
    return 0;
}




目录

Tunnel Warfare   POJ-2892

一维树状数组(单点修改,区间查询),逆序对,二分

题意

最初相邻村庄(编号从 1 → n 1 \rightarrow n 1n)之间有一条道路连接
接下来几条命令:
‘D x’: 摧毁编号为 x x x 的村庄被摧毁,即相邻两个村庄到这个村庄的道路也没了
‘R’: 修复最后摧毁的那个村庄,即相邻两个村庄到这个村庄的道路也会修复
‘Q x’: 询问标号为 x x x 的村庄最大连续可以互通的村庄长度

做法

看到这个下意识就想写线段树
线段树的话就是维护区间最大连续长度
然鹅,正在练习树状数组
最初可以初始化每个村庄的树状数组值为 1 1 1,一开始村庄都还在, u p d a t e ( x , 1 ) update(x, 1) update(x,1)
那么摧毁也就只需要 u p d a t e ( x , − 1 ) update(x, -1) update(x,1)

主要在于询问部分
询问部分需要用到二分来查询,二分区间的边界,下面详细说明
q u e r y ( x ) query(x) query(x) 可以得到 1 → x 1 \rightarrow x 1x 间,所有还在的村庄的个数,也就是说,
如果询问一个区间内村庄的个数,那么当 q u e r y ( r ) − q u e r y ( l − 1 ) = = r − l + 1 query(r) - query(l - 1) == r - l + 1 query(r)query(l1)==rl+1 时,说明这一段区间内的村庄都存在
那么为了得到包含 x x x 在内的连续村庄的个数,可以二分两次,
分别找到 L → i d L \rightarrow id Lid 最左边满足条件的 L L L, i d → R id \rightarrow R idR 最右边满足条件的 R R R
最后 R − L + 1 R - L + 1 RL+1 就是最终的答案

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxn = 5e4 + 5;

bool vis[maxn];
int tr[maxn];
int st[maxn];
int n;

int lowbit(int x) {
    return x & (-x);
}

void update(int id, int val) {
    for(int i = id; i < maxn; i += lowbit(i))
        tr[i] += val;
}

int query(int id) {
    int ans = 0;
    for(int i = id; i > 0; i -= lowbit(i))
        ans += tr[i];
    return ans;
}

void solve(int id) {
    if(!vis[id]) {
        puts("0");
        return ;
    }
    int L, R; // 最有边界
    int l = 1, r = id, mid, x = query(id);
    while(l <= r) {
        mid = (l + r) >> 1;
        if(x - query(mid - 1) >= id - mid + 1)
            L = mid, r = mid - 1;
        else 
            l = mid + 1;
    }
    l = id, r = n, x = query(id - 1);
    while(l <= r) {
        mid = (l + r) >> 1;
        if(query(mid) - x >= mid - id + 1)
            R = mid, l = mid + 1;
        else
            r = mid - 1;
    }
    printf("%d\n", R - L + 1);
}

int main() {
    int m;
    scanf("%d%d", &n, &m);
    memset(tr, 0, sizeof(tr));
    memset(vis, true, sizeof(vis));
    for(int i = 1; i <= n; ++i) update(i, 1);
    char s[2]; int id, top = 0;
    while(m--) {
        scanf("%s", s);
        if(s[0] == 'R' && top) {
            id = st[top--], vis[id] = true;
            update(id, 1);
        }else{
            scanf("%d", &id);
            if(s[0] == 'D') {
                st[++top] = id, vis[id] = false;
                update(id, -1);
            }else{
                solve(id);
            }
        }
    } 
    return 0;
}



目录

Apple Tree   POJ-3321

一维树状数组(单点修改,区间查询),dfs序(树转区间)

题意

有一棵 n n n 个结点的树, 1 1 1 为根,每个结点最初都有一颗苹果
两种命令
Q x: 询问结点 x x x 即其儿子分支所有苹果数目
C x: 如果结点 x x x 上没有苹果,则 x x x 长出一颗苹果。如果有苹果,则摘取这个苹果

做法

涉及到树的,且并不是二叉树,可以采用 d f s dfs dfs 序来进行标记
d f s dfs dfs 可以利用时间戳给结点标序,并且最后退出结点的编号 - 最初遍历到结点的编号 = 儿子个数
最初想到了 d f s dfs dfs 序但是自己认为无法找到要查询的区间
看来是对 d f s dfs dfs 序了解不太好
题解详细可以看这篇博客,写的挺好的
Apple Tree(dfs序+树状数组)
利用最初遍历的编号 i n [ u ] in[u] in[u] 和 最后退出即最大儿子的编号 o u t [ u ] out[u] out[u]
这个即为查询的时候要查询的区间,且 i n [ u ] in[u] in[u] 可以直接作为这个点的 d f s dfs dfs序编号,至于为什么,请看上面这篇博客
然后树状数组进行单点更新的区间查询是 q u e r y ( r ) − q u e r y ( l − 1 ) query(r) - query(l - 1) query(r)query(l1)

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 5;

struct node{
    int v, next;
}edge[maxn];
int head[maxn];
bool vis[maxn];
int out[maxn];
int in[maxn];
// int num[maxn];
int tr[maxn];
int n, tol, cnt;


void add(int u, int v) {
    edge[tol] = node{v, head[u]}; head[u] = tol++;
    edge[tol] = node{u, head[v]}; head[v] = tol++;
}

int lowbit(int x) {
    return x & (-x);
}

void dfs(int u, int pre) {
    in[u] = ++cnt;
    // num[cnt] = u;
    for(int i = head[u]; ~i; i = edge[i].next) {
        if(edge[i].v == pre)    continue;
        dfs(edge[i].v, u);
    }
    out[u] = cnt;
}

void update(int id, int val) {
    for(int i = id; i <= n; i += lowbit(i))
        tr[i] += val;
}

int query(int id) {
    int ans = 0;
    for(int i = id; i > 0; i -= lowbit(i))
        ans += tr[i];
    return ans;
}

int main() {
    tol = cnt = 0;
    scanf("%d", &n);
    int u, v;
    memset(head, -1, sizeof(head));
    memset(vis, true, sizeof(vis));
    for(int i = 1; i < n; ++i) {
        scanf("%d%d", &u, &v); add(u, v);
    }
    dfs(1, -1);
    for(int i = 1; i <= n; ++i)
        update(i, 1);

    char s[2]; int m, pos;
    scanf("%d", &m);
    while(m--) {
        scanf("%s%d", s, &pos);
        if(s[0] == 'Q') {
            printf("%d\n", query(out[pos]) - query(in[pos] - 1));
        }else{
            if(!vis[pos]) {
                vis[pos] = true;
                update(in[pos], 1);
            }else{
                vis[pos] = false;
                update(in[pos], -1);
            }
        }
    }
    return 0;
}


目录

Mobile phones   POJ-1195

二维树状数组模板(单点修改,区间查询)

题意

几个操作
0 S: 初始化 S × S S \times S S×S 的矩阵,全为 0 0 0
1 X Y A: 点 ( X , Y ) (X,Y) (X,Y) 上的值 + A +A +A
2 L B R T: 询问 左上角 ( L , R ) (L,R) (L,R),右下角 ( B , T ) (B,T) (B,T) 这个矩阵的元素的和
3: 退出

做法

二维树状数组模板,就不讲了

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxn = 2e3 + 5;
const ll mod = 1e9;

ll tr[maxn][maxn];
int n;

int lowbit(int x) {
    return x & (-x);
}

void update(int x, int y, int val) {
    for(int i = x; i <= n; i += lowbit(i))
        for(int j = y; j <= n; j += lowbit(j))
            tr[i][j] += val * 1ll;
}

ll query(int x, int y) {
    ll ans = 0;
    for(int i = x; i > 0; i -= lowbit(i))
        for(int j = y; j > 0; j -= lowbit(j))
            ans += tr[i][j];
    return ans;
}

ll query(int x1, int y1, int x2, int y2) {
    return query(x2, y2) - query(x1 - 1, y2) - query(x2, y1 - 1) + query(x1 - 1, y1 - 1);
}

int main() {
    int ope;
    scanf("%d%d", &ope, &n);
    for(int i = 0; i <= n; ++i)
        for(int j = 0; j <= n; ++j)
            tr[i][j] = 0;
    while(~scanf("%d", &ope) && ope != 3) {
        int x1, x2, y1, y2, val;
        if(ope == 1) {
            scanf("%d%d%d", &x1, &y1, &val);
            update(x1 + 1, y1 + 1, val);
        }else if(ope == 2) {
            scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
            printf("%lld\n", query(x1 + 1, y1 + 1, x2 + 1, y2 + 1));
        }
    }
    return 0;
}


目录

Minimum Inversion Number   HDU-1394

一维树状数组(单点修改,单点查询),逆序对,思维

题意

给一个长度为 n n n 的数组,数组值为 0 → n − 1 0 \rightarrow n-1 0n1,且不重复
每次可以将第一个位置的数移动到最后一个位置
问该数组的最小逆序对
所谓逆序对就是 i < j & & a [ i ] > a [ j ] i < j \&\& a[i] > a[j] i<j&&a[i]>a[j]

做法

求逆序对,我们需要在变插入的同时就求出当前数的逆序对数
即 ans += query(n) - query(a[i]);
query(n) 其实就等于当前已经插入的几个数
要求的是大于 a [ i ] a[i] a[i] 的数的个数,因此要用当前总数 - 小于等于 a [ i ] a[i] a[i] 的个数

  • 关键的在于如何计算每次移动后带来的贡献

    可以注意到,由于每个数都只出现了一次,且该数组是连续的
    那么每次将 x = a [ 1 ] x = a[1] x=a[1] 移动到 a [ n ] a[n] a[n]
    对于 < x < x <x 的数的贡献即为 − x -x x
    n = 6
    例如 2 5 0 3 1 4 → \rightarrow 5 0 3 1 4 2
    对于 0 , 1 0, 1 0,1 来说,都各减少了一对逆序对
    那么对于 > x > x >x 的数的贡献即为 n − ( a [ i ] + 1 ) n - (a[i] + 1) n(a[i]+1)
    例如 2 5 0 3 1 4 → \rightarrow 5 0 3 1 4 2
    对于 5 , 3 , 4 5, 3, 4 5,3,4 来说,都各增加了一对逆序对
    即对于 x x x 移至末尾,总贡献是 n − ( a [ i ] + 1 ) − a [ i ] n - (a[i] + 1) - a[i] n(a[i]+1)a[i]
    但是需要注意的一个地方是
    由于最小的数为 0 0 0,因此要对数据进行处理,都加上 1 1 1
    对于 < x < x <x 的数的贡献变为 a [ i ] − 1 a[i] - 1 a[i]1
    那么对于 > x > x >x 的数的贡献变为 n − a [ i ] n - a[i] na[i]
    即对于 x x x 移至末尾,总贡献是 n − a [ i ] − ( a [ i ] + 1 ) n - a[i] - (a[i] + 1) na[i](a[i]+1)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxn = 5e4 + 5;

int tr[maxn];
int a[maxn];
int n;

int lowbit(int x) {
    return x & (-x);
}

void update(int id, int val) {
    for(int i = id; i <= n; i += lowbit(i))
        tr[i] += val;
}

int query(int id) {
    int ans = 0;
    for(int i = id; i > 0; i -= lowbit(i))
        ans += tr[i];
    return ans;
}

int main() {
    while(~scanf("%d", &n)) {
        int ans = 0;
        for(int i = 0; i <= n; ++i) tr[i] = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]); a[i] += 1;
            ans += i - 1 - query(a[i]);  // 其实 i-1 就是目前 query(n)
            update(a[i], 1);
        }
        int minn = ans;
        for(int i = 1; i <= n; ++i) {  // 因为前面的 a[i] += 1 了
            ans += n - a[i] - (a[i] - 1);
            minn = min(minn, ans);
        }
        printf("%d\n", minn);
    }
    return 0;
}



目录

Ultra-QuickSort   POJ-2299

一维树状数组(单点修改,单点查询),逆序对,离散化

题意

求长度为 n n n 的数组,要完全排成升序数组
至少需要转换多少次
每次转换只能转换相邻的两个数

做法

其实每个数要转到对应的位置,即他在数组里逆序对的数目
主要是 a [ i ] a[i] a[i] 的大小比较大,需要先离散化
然后用树状数组求总逆序对数

代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <vector>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxn = 5e5 + 5;
const ll mod = 1e9;

vector<int> v;
int tr[maxn];
int a[maxn];
int n;

int lowbit(int x) {
    return x & (-x);
}

void update(int id, int val) {
    for(int i = id; i <= n; i += lowbit(i))
        tr[i] += val;
}

int query(int id) {
    int ans = 0;
    for(int i = id; i > 0; i -= lowbit(i))
        ans += tr[i];
    return ans;
}

int main() {
    while(~scanf("%d", &n) && n) {
        int x; ll ans = 0;
        v.clear(); v.push_back(-1);
        for(int i = 0; i <= n; ++i) tr[i] = 0;
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &x);
            v.push_back(x), a[i] = x;
        }
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
        for(int i = 1; i <= n; ++i) {
            int x = lower_bound(v.begin(), v.end(), a[i]) - v.begin();
            ans += (query(n) - query(x)) * 1ll;
            update(x, 1);
        }
        printf("%lld\n", ans);
    }
    return 0;
}


目录

Cows   POJ-2481

一维树状数组(单点修改,单点查询)

题意

简单说就是要求一个区间被多少个区间完全覆盖,且不能左右端点都重合,即完全相同的两个区间不计算

做法

可以对区间进行排序,由于要计算的是被多少个区间覆盖
那么考虑将区间最大的先记录
将右端点降序排序,这样可以保证右端点是非递增的,接下来就只需要考虑左端点已经有多少个 ≤ \leq 当前左端点的
这里可以利用树状数组计算,或者权值线段树(树状数组比较方便,偷个懒
除此之外,还要另外考虑区间完全相同的情况,区间完全相同就直接等于上一个的结果

代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define lowbit(x)   (x&(-x))
using namespace std;
typedef long long ll;
const ll INF = 1e18;
const int maxn = 1e5 + 5;

struct node{
    int l, r, id;
    bool operator < (const node &_) const {
        return r == _.r ? l < _.l : r > _.r;
    }
}a[maxn];
int sum[maxn];

void update(int id, int val) {
    for(int i = id; i < maxn; i += lowbit(i))
        sum[i] += val;
}

int query(int id) {
    int ans = 0;
    for(int i = id; i > 0; i -= lowbit(i))
        ans += sum[i];
    return ans;
}

int ans[maxn];
int main() {
    int n;
    while(~scanf("%d", &n) && n) {
        memset(sum, 0, sizeof(sum));
        for(int i = 1; i <= n; ++i) {
            scanf("%d%d", &a[i].l, &a[i].r);
            a[i].l += 1, a[i].r += 1, a[i].id = i;
        }
        sort(a + 1, a + 1 + n);
        for(int i = 1; i <= n; ++i) {
            if(a[i].l == a[i-1].l && a[i].r == a[i-1].r)
                ans[a[i].id] = ans[a[i-1].id];
            else
                ans[a[i].id] = query(a[i].l);
            update(a[i].l, 1);
        }
        for(int i = 1; i <= n; ++i)
            printf("%d%c", ans[i], i == n ? '\n' : ' ');
    }
    return 0;
}


目录

MooFest   POJ-1990

题意

做法

代码




目录

Ping pong   HDU-2492

题意

做法

代码




目录

Conturbatio   HDU-5480

题意

做法

代码




目录

Cube   HDU-3584

题意

做法

代码




目录

Japan   POJ-3067

题意

做法

代码




目录

Life is a Line   HDU-3465

题意

做法

代码




目录

Magic Ball Game   HDU-4605

题意

做法

代码




目录

1  

题意

做法

代码








目录
敌兵布阵   HDU-1166   一维树状数组模板(单点修改,单点查询)
Stars   HDU-1541   一维树状数组(单点修改,单点查询), 逆序对
Tunnel Warfare   POJ-2892   一维树状数组(单点修改,区间查询),逆序对,二分
Apple Tree   POJ-3321   一维树状数组(单点修改,区间查询),dfs序(树转区间)
Mobile phones   POJ-1195 未做   二维树状数组模板(单点修改,区间查询)
Minimum Inversion Number   HDU-1394   一维树状数组(单点修改,单点查询),逆序对,思维
Ultra-QuickSort   POJ-2299   一维树状数组(单点修改,单点查询),逆序对,离散化
Cows   POJ-2481   一维树状数组(单点修改,单点查询),排序

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
树状数组(Fenwick Tree)是一种用于快速维护数组前缀和的数据结构。它可以在 $O(\log n)$ 的时间内完成单点修改和前缀查询操作,比线段树更加简洁高效。 下面是 Java 实现的树状数组详解: 首先,在 Java 中我们需要使用数组来表示树状数组,如下: ``` int[] tree; ``` 接着,我们需要实现两个基本操作:单点修改和前缀查询。 单点修改的实现如下: ``` void update(int index, int value) { while (index < tree.length) { tree[index] += value; index += index & -index; } } ``` 该函数的参数 `index` 表示要修改的位置,`value` 表示修改的值。在函数内部,我们使用了一个 `while` 循环不断向上更新树状数组中相应的节点,直到到达根节点为止。具体来说,我们首先将 `tree[index]` 加上 `value`,然后将 `index` 加上其最后一位为 1 的二进制数,这样就可以更新其父节点了。例如,当 `index` 为 6 时,其二进制表示为 110,最后一位为 2^1,加上后变为 111,即 7,这样就可以更新节点 7 了。 前缀查询的实现如下: ``` int query(int index) { int sum = 0; while (index > 0) { sum += tree[index]; index -= index & -index; } return sum; } ``` 该函数的参数 `index` 表示要查询的前缀的结束位置,即查询 $[1, index]$ 的和。在函数内部,我们同样使用了一个 `while` 循环不断向前查询树状数组中相应的节点,直到到达 0 为止。具体来说,我们首先将 `sum` 加上 `tree[index]`,然后将 `index` 减去其最后一位为 1 的二进制数,这样就可以查询其前一个节点了。例如,当 `index` 为 6 时,其二进制表示为 110,最后一位为 2^1,减去后变为 100,即 4,这样就可以查询节点 4 的值了。 最后,我们还需要初始化树状数组,将其全部置为 0。初始化的实现如下: ``` void init(int[] nums) { tree = new int[nums.length + 1]; for (int i = 1; i <= nums.length; i++) { update(i, nums[i - 1]); } } ``` 该函数的参数 `nums` 表示初始数组的值。在函数内部,我们首先创建一个长度为 `nums.length + 1` 的数组 `tree`,然后逐个将 `nums` 中的元素插入到树状数组中。具体来说,我们调用 `update(i, nums[i - 1])` 来将 `nums[i - 1]` 插入到树状数组的第 `i` 个位置。 到此为止,我们就完成了树状数组的实现。可以看到,树状数组的代码比线段树要简洁很多,而且效率也更高。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值