博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
树状数组
阅读量:4460 次
发布时间:2019-06-08

本文共 5962 字,大约阅读时间需要 19 分钟。

最近几天在学树状数组,做一个小总结!

树状数组

  • 引入(应用)
    • 给定一个序列,动态的修改某一个数、询问一段数字的和
    • 一般算法:修改1次O(1),查询一次O(m),查询n次O(m*n)容易超时
  • 模型

    使用c数组

  • 基本定义

    • c[i]记录了多少个数的和
      由图:
      C1=a1
      C2=a1+a2
      C3=a3
      C4=a1+a2+a3+a4
      C5=a5
      C6=a5+a6
      ……
      C8=a1+a2+a3+a4+a5+a6+a7+a8
      ……
      C[2^n]=a[1]+a[2]+….+a[2^n]
      可知:
      C[n]表示2^k个数的和(k为n对应的2进制数的末尾0的个数),其中2 ^ k = n & (n ^ n – 1) == n & -n (lowbit函数)
    • 如何修改单点
      修改了某个a[i],就需改动所有包含a[i]的c[j],即修改从子节点到父节点路径上的所有c
      父节点:
      第X项的父(后继)节点是第X + (X & -X)项(由图可分析)
    • 如何求和
      求和(前驱和)直接使用一次for循环,循环的次数为x的二进制表示中1的个数,即x前驱就是每次砍掉x二进制的最后一位1后的数
  • 各功能的伪代码

int lowbit(int x){
//求和范围/ return x & (x ^ (x - 1));}void buildtree(){
//建立c数组/ for (int i = 1; i <= n; ++i){ int l = lowbit(i); for (int j = i - l + 1; j <= i; ++j) c[i] += a[j]; }}void add(int k, int d){
//修改:给k加上d/ if (k > n) return ; c[k] += d; add(k + lowbit(k), d);}int sum(int s, int t){
//求区间s~t的和/ int ans1 = 0, ans2 = 0; for (int i = t; i >= 1; i -= lowbit(i)) ans1 += c[i]; for (int i = s - 1; i >= 1; i -= lowbit(i)) ans2 += c[i]; return ans1 - ans2;}
  • 练习

基本树状数组的练习

#include 
#include
#include
#define MAXN 100100using namespace std;int n, m;int s, t;int k, d;string ch;int a[MAXN], c[MAXN];int lowbit(int x){ return x & (x ^ (x - 1));}void buildtree(){ for (int i = 1; i <= n; ++i){ int l = lowbit(i); for (int j = i - l + 1; j <= i; ++j) c[i] += a[j]; }}void add(int k, int d){ if (k > n) return ; c[k] += d; add(k + lowbit(k), d);}int sum(int s, int t){ int ans1 = 0, ans2 = 0; for (int i = t; i >= 1; i -= lowbit(i)) ans1 += c[i]; for (int i = s - 1; i >= 1; i -= lowbit(i)) ans2 += c[i]; return ans1 - ans2;}int main(){ scanf("%d", &n); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); buildtree(); scanf("%d", &m); for (int i = 1; i <= m; ++i) { cin >> ch; if (ch == "ADD") { scanf("%d %d", &k, &d); add(k, d); } if (ch == "SUM") { scanf("%d %d", &s, &t); printf("%d\n", sum(s, t)); } } return 0;}

异或求和问题不清楚的手完一下

#include 
#include
#include
#define MAXN 100100using namespace std;int n, m;int l, r;int x, y;bool bj;int a[MAXN];int c[MAXN];int lowbit(int x){ return x & (x ^ (x - 1));}void buildtree(){ for (int i = 1; i <= n; ++i){ int l = lowbit(i); for (int j = i - l + 1; j <= i; ++j) c[i] ^= a[j]; }}int sum(int l, int r){ int ans1 = 0, ans2 = 0; for (int i = r; i >= 1; i -= lowbit(i)) ans1 ^= c[i]; for (int i = l - 1; i >= 1; i -= lowbit(i)) ans2 ^= c[i]; return ans1 ^ ans2;}void change(int x, int y){ int ch = sum(1, x) ^ sum(1, x - 1); for (int i = x; i <= n; i += lowbit(i)) c[i] ^= ch, c[i] ^= y;}int main(){ scanf("%d %d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", &a[i]); buildtree(); for (int i = 1; i <= m; ++i) { cin >> bj; if (bj) { scanf("%d %d", &l, &r); printf("%d\n", sum(l, r)); } if (!bj) { scanf("%d %d", &x, &y); change(x, y); } } return 0;}

这是一个重点

直接遍历遍数组,每个位置先add(a[i], 1),然后再查询sum(a[i]),即到第i位为止的逆序对个数为(i - sum(a[i]))
注意:c数组初始化均为0

//不用离散化/#include 
#include
#include
#define MAXN 10000000using namespace std;int n;int a[MAXN];int c[MAXN];int lowbit(int x) { return x & (x ^ (x - 1));}void add(int x) { if (x > 100000) return ; c[x]++; add(x + lowbit(x));}long long sum(int x) { long long y = 0; for (int i = x; i >= 1; i -= lowbit(i)) y += c[i]; return y;}int main(){ scanf("%d", &n); long long ans = 0; for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), a[i]++; for (long long i = 1; i <= n; ++i) { add(a[i]); ans += (i - sum(a[i])); } printf("%lld", ans); return 0;}

一道树状数组求逆序对的板子题

与上一题不能用离散化不同,此题数据量较大不用离散化会RE,放上离散化的代码

#include 
#include
#include
#include
#define L 500000 + 10using namespace std;struct node{ int v, pos;}a[L];long long n, ans;int num[L], c[L];inline bool comp(node a, node b) { return a.v < b.v;}inline int lowbit(int x) { return x & (-x);}inline void add(int x) { for (int i = x; i <= L; i += lowbit(i)) c[i]++;}inline long long sum(int x) { long long s = 0; for (int i = x; i; i -= lowbit(i)) s += c[i]; return s;}int main() { while (scanf("%d", &n) && n != 0) { memset(a, 0, sizeof(a)); memset(num, 0, sizeof(num)); memset(c, 0, sizeof(c)); ans = 0; for (int i = 1; i <= n; ++i) scanf("%d", &a[i].v), a[i].pos = i; sort(a + 1, a + 1 + n, comp); for (int i = 1; i <= n; ++i) num[a[i].pos] = i; for (int i = 1; i <= n; ++i) { add(num[i]); ans += (i - sum(num[i])); } printf("%lld\n", ans); } return 0;}

这道题目的输入数据是多组的

注意可仅根据x坐标来设立c数组
对于每一次+1操作应该操作到最大值MAXN
注意数组的大小也应开到MAXN
一道树状数组基础题……

#include 
#include
#include
#define L 20000 + 10#define MAXN 32000 + 5using namespace std;int n, a, b;int ans[MAXN], c[MAXN];int lowbit(int x) { return x & (-x);}void add(int x) { while(x <= MAXN){ ++c[x]; x += lowbit(x); } }int query(int x) { int q = 0; while(x > 0){ q += c[x]; x -= lowbit(x); } return q;}int main() { while (scanf("%d", &n) != EOF) { memset(c, 0, sizeof(c)); memset(ans, 0, sizeof(ans)); for (int i = 1; i <= n; ++i) { scanf("%d %d", &a, &b); a++; ans[query(a)]++; add(a); } for (int i = 0; i < n; ++i) printf("%d\n", ans[i]); } return 0;}

总结未做题

简单:
POJ 1195 Mobile phones
中等
POJ 2155 Matrix
POJ 3321 Apple Tree
POJ 1990 MooFest
难题:
POJ 2464 Brownie Points II

——CYCKCN

转载于:https://www.cnblogs.com/CYCKCN/p/6371520.html

你可能感兴趣的文章
java运行时内存分类
查看>>
为什么说 Git 比 SVN 更好
查看>>
1.基础数据类型的初识 字符串 bool 整型 if else elif
查看>>
【设计模式】4、原型模式
查看>>
进入meta模式关闭背光灯
查看>>
webstorm上svn的安装使用
查看>>
【JEECG技术文档】数据权限自定义SQL表达式用法说明
查看>>
使用 Bootstrap Typeahead 组件
查看>>
linux_cacti 配置之 安装snmp 服务
查看>>
201407-至今
查看>>
c# 应用事务
查看>>
优化杭州某著名电子商务网站高并发千万级大型数据库经验之- SQL语句优化(转)...
查看>>
WPF——TargetNullValue(如何在绑定空值显示默认字符)
查看>>
Linux之crontab
查看>>
清除浮动
查看>>
CenOS+宝塔(模拟)上线博客项目
查看>>
loadrunner Vugen-Tools General-Options-Replay设置
查看>>
redis限频
查看>>
Floyd判圈算法
查看>>
接口,lambda表达式与内部类(二)
查看>>