树状数组

树状数组及其简单扩展

丶灬走出姿态 提交于 2019-12-01 07:24:25
树状数组及其简单扩展 不对树状数组做详细讲解,只对应用类型做总结. 一维树状数组 单点修改,区间查询 由于树状数组维护的是前缀信息,所以区间查询直接利用前缀特性相减即可. 单点修改只需从开始位置向后更新即可. 区间修改,单点查询 树状数组只支持单点修改,那么如何处理区间修改呢? 考虑常见的区间修改转单点修改的套路:差分. 而差分数组的前 \(n\) 项和也恰好就是真正的 \(v_i\) . 所有直接差分去做即可. 区间修改,区间查询 同理,先差分,差分后区间修改是没有区别的. 而这里我们发现,我们只能得到对应的单点值,却不能直接得到对应的区间和. 难道要套一种可以区间求和的数据结构嘛?答案是否定的. 我们发现,我们要求的其实是这个: \[\sum_{i=l}^r{\sum_{j=1}^i}{d_j}\] 其中, \(d_j\) 就是差分数组.对于内层求和显然可以直接在树状数组中查询得到. 那么怎么处理外层呢?再 \(for\) 一遍是显然不可能的. 所以我们重新考虑: 令 \(sum_i\) 表示前缀和. \[\begin{array}{c}{\operatorname{sum}[i]=\sum_{j=1}^{i} v[j]+\sum_{j=1}^{i} \operatorname{d}[j] *(i-j+1)} \\ {\operatorname{sum}[i]=\sum_{j

浅谈二维树状数组

帅比萌擦擦* 提交于 2019-12-01 07:18:19
①前置知识 静态二维前缀和: ①:预处理递推:f[ i ][ j ] = f[ i - 1 ][ j ] + f[ i ][ j -1 ] - f[ i - 1][ j - 1] + val[ i ][ j ]. ②:左上角( X 1 , Y 1 ),右下角( X 2 , Y 2 ),这一段的区间和:f[ X 2 ][ Y 2 ] - f[ X 2 ][ Y 1 - 1] -f[ X 1 - 1][ Y 2 ] + f[ X 1 - 1 ][ Y 1 - 1 ]. 其实画一下图就很好理解了,具体详细教程从dalao的这篇blog: 传送门. ②二维树状数组 I.(单点修改,区间查询) 考虑一个点( X , Y )的存在,我们过这个点分别做X轴,Y轴的平行线,把这个点看做矩形的左上角,发现它只对它右下角的矩形才产生贡献.我们不由地联想到树状数组,用tree[ x ][ y ]的二维去维护点对'x下方', 'y右方'的贡献(想一想lowbit的作用).又因为树状数组求的和为前缀和,所以只要套静态二维前缀的的区间查询公式即可. 以LOJ的板子为例: #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define e exit(0) #define re register #define

[洛谷P3374]【模板】树状数组 1

做~自己de王妃 提交于 2019-12-01 04:36:30
题目来源: https://www.luogu.org/problem/P3374 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入格式 第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。 接下来M行每行包含3个整数,表示一个操作,具体如下: 操作1: 格式:1 x k 含义:将第x个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和 输出格式 输出包含若干行整数,即为所有操作2的结果。 输入输出样例 输入 5 5 1 5 4 2 3 1 1 3 2 2 5 1 3 -1 1 4 2 2 1 4 输出 14 16 说明/提示 时空限制:1000ms,128M 数据规模: 对于30%的数据:N<=8,M<=10 对于70%的数据:N<=10000,M<=10000 对于100%的数据:N<=500000,M<=500000 #include <bits/stdc++.h> using namespace std; const int maxn = 5e5+1; int a[maxn], c[maxn], n, m; int lowbit(int x){ return x&(-x); } void update

树状数组

天涯浪子 提交于 2019-12-01 04:34:34
知识讲解 参考刘汝佳-陈峰《算法竞赛入门经典训练指南》树状数组章节 https://www.cnblogs.com/xenny/p/9739600.html 模板题 1.P3374 【模板】树状数组 1 2.HDU1166 敌兵布阵 来源: https://www.cnblogs.com/gdgzliu/p/11654311.html

【树状数组】P1972 [SDOI2009]HH的项链

断了今生、忘了曾经 提交于 2019-12-01 04:30:17
1 #include<iostream> 2 #include<string> 3 #include<queue> 4 #include<stack> 5 #include<vector> 6 #include<map> 7 #include<cstdio> 8 #include<cstdlib> 9 #include<algorithm> 10 #include<set> 11 #include<iomanip> 12 #include<cstring> 13 #include<cmath> 14 #include<limits> 15 using namespace std; 16 17 #define au auto 18 #define debug(i) cout<<"debug: "<<i<<endl 19 #define mfor(i,a,b) for(register int i=(a);i<=(b);i++) 20 #define mrep(i,a,b) for(register int i=(a);i>=(b);i--) 21 #define LLL __int128 22 #define Re register 23 #define il inline 24 #define mem(a,b) memset(a,(b),sizeof(a)) 25 typedef

树状数组(小总结)

大兔子大兔子 提交于 2019-11-30 19:55:07
树状数组 经典应用类型 求区间[l ,r]的和(乘积等);修改单点的值 求单点的值,修改区间[l ,r]的值 (类似线段树) 特性 类似 线段树 ,但是常数更小,实现起来更简单,当然,功能不如线段树 数据结构 数组a[1…n]为原始数据 数组d[1…n]记录着区间和(乘积等) 数组d对应数组a的关系如下: 设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。因为这个区间最后一个元素必然为Ax, 所以很明显: d[n] = A(n – 2^k + 1) + ... + A[n] 实现方法 基本函数 计算 2^k 的函数 int lowbit ( int x ) { return x & ( - x ) ; } 插入操作(将a[n]加v,维护d数组) void modify ( int x , int v ) { /*Add v y a[x]*/ while ( x <= n ) { d [ x ] + = v ; x + = lowbit ( x ) ; } } 查询操作(返回区间[1…x]的和 前缀和 ) int query ( int x ) { /*Query from a[x] + ... + a[y]*/ int sum = 0 ; while ( x ) { sum + = d [ x ] ; x - = lowbit ( x ) ; }

hdu-6230 主席树/树状数组+离线

我的未来我决定 提交于 2019-11-30 16:05:00
  刚开始是听说了要用manacher采取写的,但是不难看出要使用manacher,因为题目要求的1个半回文串,其实就是两个回文中心的交集,你要找两个回文中心,让它们的回文半径可以互相覆盖即可。问题转化成了对于一个回文中心,它覆盖的左侧的所有回文中心中( 向右覆盖的范围超过i )这样的点的数量。这样就变成了求区间中大于x的数的个数,可以用主席树解决。 #include<iostream> #include<cstring> #include <string> #include<algorithm> using namespace std; typedef long long ll; const int maxn=1e6+20; const int maxm=2e7+20; char s[maxn],s_new[maxn]; int p[maxn]; int init() { int len=(int)strlen(s); s_new[0]='$'; s_new[1]='#'; int j=2; for(int i=0;i<len;i++) { s_new[j++]=s[i]; s_new[j++]='#'; } s_new[j]=0; return j; } int manacher() { int len=init(); int id=0,mx=0; for(int i=1;i

逆序对

不羁岁月 提交于 2019-11-30 12:51:27
今天想写一个专题:如何求一串数中的逆序对个数。 具体来讲,一共有两种比较好的方法: 归并排序 树状数组 两种方法的比较 先来比较一下两个方法: 方法 时间复杂度 空间复杂度 代码长度 理解难度 归并排序 相比较长 (除非你用STL) 容易 树状数组 短小精悍 (用不到STL) 一般 好吧,其实并没有多大区别,但是还是建议这两个方法都学一下 归并排序 其实就是在归并排序的时候发现一个较大的数在前面,计算一个逆序对个数,废话不多说,直接看代码理解吧。 代码 void Msort(int l,int r) { int ret[500002]; if(l!=r) { int mid=(l+r)/2; Msort(l,mid);Msort(mid+1,r); int i=l,j=mid+1,k=l; while(i<=mid&&j<=r) { if(a[i].num<=a[j].num) { ret[k]=a[i].num; i++;k++; }else { ret[k]=a[j].num; j++;k++;ans=(ans+mid-i+1)%P;//加上逆序对个数(模P) }; }; while(i<=mid) { ret[k]=a[i].num; i++;k++; }; while(j<=r) { ret[k]=a[j].num; j++;k++; }; for(int i=l;i<=r

模板 - 树状数组

怎甘沉沦 提交于 2019-11-30 12:26:28
区间加值,区间求和的树状数组,通过差分来实现。 并没有线段树那么通用。 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll bit[100005]; ll bit2[100005]; int n; void add(int x, ll v) { for(; x <= n; x += x & -x) bit[x] += v; } ll sum(int x) { ll res = 0; for(; x; x -= x & -x) res += bit[x]; return res; } void add2(int x, ll v) { for(; x <= n; x += x & -x) bit2[x] += v; } ll sum2(int x) { ll res = 0; for(; x; x -= x & -x) res += bit2[x]; return res; } ll ans(int n) { return 1ll * (n + 1) * sum(n) - sum2(n); } int main() { #ifdef Yinku freopen("Yinku.in", "r", stdin); #endif // Yinku int m; scanf("%d%d", &n, &m);

「树状数组」楼兰图腾

天涯浪子 提交于 2019-11-30 10:25:50
楼兰图腾 原题链接: 楼兰图腾 题目大意 给你 \(n\) 个数,再给你两种限制条件,问你在符合这两种限制条件的情况,每种限制最多有多少个数 题目题解 这个题可以拓展到一些需要用到离散化的题 这个题很简单,两个限制条件,可以这么认为 限制条件1:枚举所有的数,然后找左边比它大的,找右边比它大的 限制条件2:枚举所有的书,然后处理方式与上面相反 当然方式可以更简单一点,只用计算一种即可,另外一种肯定是左右剩下的数组成(不懂?不懂可以看看代码),那么剩下怎么办呢?很简单,直接用 \(l_i \times r_i\) 就可以了(想想,为什么?答案很显然) 答案会爆int 这里要开long long 代码如下 //#define fre yes #include <cstdio> const int N = 200005; int arr[N], r[N], l[N], c[N]; namespace BIT { int c[N]; int lowbit(int x) { return x & (-x); } inline void init(int n) { for (int i = 1; i <= n; i++) { c[i] = 0; } } int ask(int x) { int res = 0; while(x) { res += c[x]; x -= lowbit(x); }