ACM模板


目录

  • 聊聊前缀和
  • 什么是树状数组?
  • 树状数组相关操作
  • 局限性
  • 差分在树状数组中的应用
    • 区间更新、单点查询
    • 区间更新、区间查询
  • 树状数组应用

聊聊前缀和

比如数组

int a[7]={1,2,3,4,5,6,7}

如果需询问数组从第l个数到第r个数的和暴力做法时间复杂度为O(n)O(n)O(n)

不过我们可以预处理一个前缀和数组

int b[7]={1,3,6,10,15,21,28}

比如要询问[l,r]区间的和我们可以这样做b[r]-b[l-1]这也时间复杂度为O(1)O(1)O(1)

但是问题来了,如果我们要既要修改数组中元素的值,有要进行上述区间查询操作呢?

我们发现每次我们修改原数组中元素的值时间复杂度为O(1)O(1)O(1)但是如果修改前缀和数组中元素的值时间复杂度将会退化到O(n)O(n)O(n)

总结一下:

数组 修改元素的值时间复杂度 区间求和时间复杂度
原数组 O(1) O(n)
前缀和数组 O(n) O(1)

我们可以发现如果需要单点更新区间查询两种操作时间复杂度都是O(n)O(n)O(n)

什么是树状数组?

树状数组是一种便于进行单点更新区间查询的数据结构

树状数组相关操作

  1. 二进制中最后一个1——lowbitlowbitlowbit
   int lowbit(int x){return x&-x;}
  1. 单点更新

    我们对数组位置为x的元素加上c

   //树状数组为tree,数组元素个数为n,数组下标从0开始void add(int x,int c){for(;x<=n;x+=lowbit(x)) tree[x]+=c;}
  1. 区间求和
  //求出[1,x]数组中的总和即前缀和int sum(int x){int res=0;for(;x;x-=lowbit(x)) res+=tree[x];return res;}

局限性

我们很容易发现上述树状数组只适用于单点更新区间查询,但是如果是区间修改单点查询好像力不从心

差分在树状数组中的应用

告诉你个好消息如果有差分的介入,那么树状数组可以进行区间更新单点查询当然也可以进行更厉害的区间更新区间查询

区间更新、单点查询

我们把tree[]tree[]tree[]数组构造成一个差分数组还是看题吧

题目

给定长度为N的数列A,然后输入M行操作指令。

第一类指令形如“C l r d”,表示把数列中第l~r个数都加d。

第二类指令形如“Q X”,表示询问数列中第x个数的值。

对于每个询问,输出一个整数表示答案。

输入格式

第一行包含两个整数N和M。

第二行包含N个整数A[i]。

接下来M行表示M条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100010;
ll tree[N];
int n,m;
int lowbit(int x)
{return x&-x;
}
void add(int x,int c)
{for(;x<=n;x+=lowbit(x)) tree[x]+=c;
}
ll sum(int x)
{int res=0;for(;x;x-=lowbit(x)) res+=tree[x];return res;
}
int main()
{cin>>n>>m;for(int i=1;i<=n;i++){int a;cin>>a;add(i,a);add(i+1,-a);}while(m--){char t;cin>>t;if(t=='Q'){int x;cin>>x;cout<<sum(x)<<endl;}else{int a,b,c;cin>>a>>b>>c;add(a,c);add(b+1,-c);}}return 0;
}

我们可以发现对于上述代码即在建树的过程中建成差分树的形式即可

区间更新、区间查询

原数组a[],对于区间更新我们可以维护一个差分数组b[]

如果我们维护数组a的前缀和我们可以发现有下面等式:
∑i=1xai=∑i=1x∑j=1ibi=∑i=1x(x−i+1)bi\sum_{i=1}^x a_i=\sum_{i=1}^x\sum_{j=1}^i b_i=\sum_{i=1}^x(x-i+1)b_i i=1∑x​ai​=i=1∑x​j=1∑i​bi​=i=1∑x​(x−i+1)bi​
变换一下:
∑i=1xai=(x+1)∑i=1xbi−∑i=1xbi×i\sum_{i=1}^x a_i=(x+1)\sum_{i=1}^x b_i-\sum_{i=1}^x b_i×i i=1∑x​ai​=(x+1)i=1∑x​bi​−i=1∑x​bi​×i

于是我们可以维护两个差分树状数组tree1[]tree1[]tree1[]维护$ bi、、、 tree2[]维护维护维护i*bi$

给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一:

1、“C l r d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。

2、“Q l r”,表示询问 数列中第 l~r 个数的和。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数N,M。

第二行N个整数A[i]。

接下来M行表示M条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>using namespace std;typedef long long ll;const int N=100010;int n,m;
ll tree1[N],tree2[N];  //维护b[i]  维护i*b[i]
int lowbit(int x)
{return x&-x;
}void add(ll tree[],int x,ll c)
{for(;x<=n;x+=lowbit(x)) tree[x]+=c;
}ll sum(ll tree[],int x)
{ll res=0;for(;x;x-=lowbit(x)) res+=tree[x];return res;
}
ll prefix_sum(int x)
{return (x+1)*sum(tree1,x)-sum(tree2,x);
}int main()
{scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){int a;scanf("%d",&a);add(tree1,i,a);add(tree1,i+1,-a);add(tree2,i,1ll*i*a);add(tree2,i+1,-1ll*(i+1)*a);}while(m--){char t;int l,r;cin>>t>>l>>r;if(t=='Q'){scanf("%d%d",&l,&r);cout<<prefix_sum(r)-prefix_sum(l-1)<<endl;}else{int d;scanf("%d",&d);add(tree1,l,d),add(tree1,r+1,-d);add(tree2,l,l*d),add(tree2, r+1,-1ll*(r+1)*d);}}return 0;
}

树状数组应用

  1. 区间更新区间查询

  2. 逆序对:首先有一种求逆序对的方法:开一个数组cnt[n]​然后遍历数组中的每一个数,在cnt[]数组中留下标记:比如数组中第i个元素为x,那就cnt[x]=1,所谓逆序即:我比你先出现而且比你大,对于上述例子所谓比你大即在[x+1,n]这个区间中的值,所谓比你先出现即在第i-1次遍历时被标记过。转化一下相当于求cntcntcnt数组中[x+1,n]区间内的和

  3. 第k小的数

【模板】一维树状数组相关推荐

  1. P3374 【模板】树状数组 1

    P3374 [模板]树状数组 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示 ...

  2. 最长上升子序列三种模板(n^2模板,二分模板,树状数组模板)

    最长上升子序列(LIS)是动态规划的入门.总结下来,经常用的模板一共有三种,分别为n^2模板,二分模板,树状数组模板. n^2模板代码如下: //n^2算法,本质就是dp,采用二重循环的方式.对于数据 ...

  3. P3374 【模板】树状数组 1( 单点修改 + 区间查询 )

    题目链接:点击进入 题目 思路深入学习 树状数组实现-> 单点修改 + 区间查询 c [ i ] = a ( i - 2 ^ k + 1 ) + - + a [ i ] ( 设节点编号为 i , ...

  4. 洛谷P3374 【模板】树状数组 1

    题目链接:[模板]树状数组 1 - 洛谷 模板题就不多说了 ac代码: #include <cstdio> #include <iostream> #include <a ...

  5. [P3374 【模板】树状数组 1](单点修改,区间查询)

    *P3374 [模板]树状数组 1* 第一道线段树的题,很好的板子题,中文体面就不过多解释了. 直接上代码(注释很详细了,前提学过线段树) #include<bits/stdc++.h> ...

  6. 【模板】树状数组 2

    题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的值 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. ...

  7. 洛谷P3368 【模板】树状数组 2(Python和C++代码)

    ##就是常规写法 用树状数组维护一个差分数组的前缀和,因为可推得若b[i]=a[i]-a[i-1],则a[i]=b[1]+-+b[i] (b[1]=a[1]-a[0],a[0]=0) . 可发现a[i ...

  8. 树状数组 java_算法模板之树状数组

    什么是树状数组? ​树状数组就是通过数组来模拟一种树形结构,这种树形结构能够维护区间信息.同样类似的数据结构还有线段树,线段树与树状数组相比,它的结点更多,也就是说线段树的常数更大. ​线段树是通过把 ...

  9. 树状数组c语言模板,【树状数组】Cows (POJ2481) PASCAL 解题报告

    [树状数组]Cows (POJ2481) Time Limit:1000MS Memory Limit:65536K Total Submit:16 Accepted:8 Description [问 ...

最新文章

  1. OpenCV中Mat的属性
  2. sql between包括两端吗_SQL简单查询
  3. ETL异构数据源Datax_工具部署_02
  4. python的JSON模块和OS模块
  5. Python之 类属性和类方法
  6. Java实现 LeetCode 686 重复叠加字符串匹配
  7. springboot中对各个层的理解以及流程
  8. stm32f103c8t6串口数据包收发
  9. ChatGPT学习研究总结
  10. 8月教材有变,如何应对PMP考试
  11. 项目管理领域关键知识点之横道图和网络图
  12. php获取ascii的值,php chr() ord() 获取ASCII编码 中文截取
  13. 服务器攻击瘫痪,网站瘫痪,企业如何应对频发
  14. iWO(联通3G详单及套餐使用情况查询工具)更新至v0.8
  15. Qt通讯录(语音输入+发邮件+发短信+头像)
  16. 宿舍怎样推行5s管理制度?
  17. gta5股票服务器维护,各位大佬问一下GTA5 股票里的BAWSAQ为什么一直维护啊
  18. 机器学习实战之路 —— 5 SVM支持向量机
  19. PHP根据经纬度计算两地之间的距离
  20. ZBrush主要干什么工作?ZBrush的发展方向,我现在开始学习还能赶上好时候吗?

热门文章

  1. js vue中得延时器_Vue.js实现时分秒倒计时
  2. 常用计算机二级函数,计算机二级MS office常用函数
  3. php 数据导出到excel文件,PHP导出数据到excel文件
  4. 用python实现流程自动化_Python自动化开发 - 流程控制
  5. [Swagger2]SpringBoot集成Swagger
  6. [C++STL]常用排序算法
  7. [蓝桥杯][算法提高VIP]摆花-多重背包计数问题
  8. [蓝桥杯2016初赛]报纸页数-生活常识+思维
  9. oracle 取英文排序,Oracle中中文、数字,英文混杂形式的字段进行排序的方法
  10. workman php 安装,workerman安装及遇到的问题解决