acwing算法提高之数据结构--树状数组

123 篇文章 1 订阅

1 介绍

本专题用来记录树状数组相关题目。

lowbit(x)操作,求数 x二进制表示中最低位的1的值,

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

树状数组:用来快速计算动态前缀和的数据结构。

c[x]的表示原数组以第x个数结尾,且区间长度为lowbit(x)的区间的和,即c[x - lowbit(x) + 1] ~ c[x]

在这里插入图片描述

树状数组支持两种操作:

  1. 单点修改,比如a[i] += x
  2. 区间查询,比如求a[l~r]的和

见上图,修改a[5]的值,则需要递次修改c[5], c[6], c[8]的值。那么修改操作的代码如下,

//a[x] += k
void add(int x, int k) {
  while (x <= n) {  // 不能越界。n就是最大值,总共n个数,编号分别为1,2,3...,n
    c[x] = c[x] + k;
    x = x + lowbit(x);
  }
}

//或可以写成
void add(int x, int k) {
	for (int i = x; i <= n; i += lowbit(i)) {
		c[i] += k;
	}
}

见上图,查询前缀和a[1~5],则需要递次遍历c[5], c[4], c[0],将它们的值累加就是前缀和。那么求前缀和的代码如下,

int getsum(int x) {  // a[1]..a[x]的和
  int ans = 0;
  while (x >= 1) {
    ans = ans + c[x];
    x = x - lowbit(x);
  }
  return ans;
}

//或可以写成
int getsum(int x) {
	int ans = 0;
	for (int i = x; i >= 1; i -= lowbit(i)) {
		ans += c[i];
	}
	return ans;
}

2 训练

题目1241楼兰图腾

C++代码如下,

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 200010;

int n;
int a[N]; //a[x] = 2表示数x出现了2次
int tr[N];
int Greater[N], lower[N];

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

void add(int x, int c) {
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

int sum(int x) {
    int res = 0;
    for (int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

int main() {
    scanf("%d", &n);
    
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    
    for (int i = 1; i <= n; ++i) {
        int y = a[i];
        Greater[i] = sum(n) - sum(y);
        lower[i] = sum(y - 1);
        add(y, 1);
    }
    
    memset(tr, 0, sizeof tr);
    LL res1 = 0, res2 = 0;
    for (int i = n; i; --i) {
        int y = a[i];
        res1 += Greater[i] * (LL)(sum(n) - sum(y));
        res2 += lower[i] * (LL)(sum(y - 1));
        add(y, 1);
    }
    
    printf("%lld %lld\n", res1, res2);
    
    return 0;
}

题目2241一个简单的整数问题

C++代码如下,

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;

int n, m;
int a[N];
LL c[N];

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

void add(int x, int k) {
    for (int i = x; i <= n; i += lowbit(i)) c[i] += k;
}

LL getsum(int x) {
    LL res = 0;
    for (int i = x; i >= 1; i -= lowbit(i)) res += c[i];
    return res;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    
    for (int i = 1; i <= n; ++i) add(i, a[i] - a[i-1]);
    
    for (int i = 0; i < m; ++i) {
        string op;
        cin >> op;
        if (op == "C") {
            int l, r, d;
            cin >> l >> r >> d;
            add(l, d);
            add(r + 1, -d);
        } else {
            int x;
            cin >> x;
            cout << getsum(x) << endl;
        }
    }
    
    return 0;
}

题目3243一个简单的整数问题2

C++代码如下,

//MYANS
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;
LL a[N];
LL c1[N], c2[N];
int n, m;

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

void add(LL c[], int x, LL k) {
    for (int i = x; i <= n; i += lowbit(i)) {
        c[i] += k;
    }
    return;
}

LL getsum(LL c[], int x) {
    LL res = 0;
    for (int i = x; i >= 1; i -= lowbit(i)) {
        res += c[i];
    }
    return res;
}

LL get_query(int x) {
    return getsum(c1, x) * (x + 1) - getsum(c2, x);
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    
    for (int i = 1; i <= n; ++i) {
        int t = a[i] - a[i-1];
        add(c1, i, t);
        add(c2, i, (LL)t * i);
    }
    
    while (m--) {
        string op;
        cin >> op;
        if (op == "Q") {
            int l, r;
            cin >> l >> r;
            cout << (LL)get_query(r) - get_query(l - 1) << endl;
        } else {
            int l, r, d;
            cin >> l >> r >> d;
            add(c1, l, d), add(c1, r + 1, -d);
            add(c2, l, l * d), add(c2, r + 1, (r + 1) * -d);
        }
    }
    
    return 0;
}

题目4244谜一样的牛

C++代码如下,

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
int h[N];
int ans[N];
int tr[N];

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

void add(int x, int c) {
    for (int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

int sum(int x) {
    int res = 0;
    for (int i = x; i >= 1; i -= lowbit(i)) res += tr[i];
    return res;
}

int main() {
    scanf("%d", &n);
    for (int i = 2; i <= n; ++i) scanf("%d", &h[i]);
    
    for (int i = 1; i <= n; ++i) tr[i] = lowbit(i);
    
    for (int i = n; i >= 1; --i) {
        int k = h[i] + 1;
        int l = 1, r = n;
        while (l < r) {
            int mid = l + r >> 1;
            if (sum(mid) >= k) r = mid;
            else l = mid + 1;
        }
        ans[i] = r;
        add(r, -1);
    }
    
    for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
    
    return 0;
}

用平衡树做这道题,超时了,通过了 8/10个数据。C++代码如下,

#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>

using namespace std;

const int N = 100010;
int a[N];
int res[N];
set<int> s;
int n;

int main() {
    cin >> n;
    
    for (int i = 2; i <= n; ++i) {
        cin >> a[i];
    }   
    
    for (int i = 1; i <= n; ++i) s.insert(i);
    
    for (int i = n; i >= 1; --i) {
        int k = a[i];
        //平衡树中第k小的数
        auto it = s.begin();
        advance(it, k);
        res[i] = *it;
        s.erase(it);
    }
    
    for (int i = 1; i <= n; ++i) cout << res[i] << endl;
    
    return 0;
}

3 参考

树状数组-OI WiKi

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
十大经典排序算法是指在计算机科学中常用的排序算法,它们分别是: 1. 冒泡排序(Bubble Sort):重复地比较相邻的两个元素,将较大的元素逐渐向右移动。 2. 选择排序(Selection Sort):每次从未排序的部分选择最小(或最大)的元素,并放在已排序的部分的末尾。 3. 插入排序(Insertion Sort):将未排序的元素逐个插入到已排序的部分中的正确位置。 4. 希尔排序(Shell Sort):将待排序的数组按照一定步长进行分组,对每组进行插入排序,逐渐减小步长。 5. 归并排序(Merge Sort):将待排序的数组递归地分成两半,对每一半进行排序,然后合并两个有序数组。 6. 快速排序(Quick Sort):选择一个基准元素,将数组划分为两部分,左边部分都小于基准,右边部分都大于基准,递归地对两部分进行排序。 7. 堆排序(Heap Sort):将待排序的数组构建成一个最大堆(或最小堆),然后依次取出堆顶元素并调整堆结构。 8. 计数排序(Counting Sort):统计数组中每个元素出现的次数,然后根据统计结果对元素进行排序。 9. 桶排序(Bucket Sort):将待排序的数组划分为多个桶,对每个桶中的元素进行排序,最后将桶中的元素按顺序合并。 10. 基数排序(Radix Sort):按照元素的位数,将待排序的数组从低位到高位进行排序。 以上是十大经典排序算法,每种算法都有其适用的场景和性能特点,选择合适的排序算法可以提高程序的效率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YMWM_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值