树状数组

树状数组

爱⌒轻易说出口 提交于 2019-12-06 10:52:53
树状数组 #define ll long long #define lowbit(i) (i&-i) const int maxn=1e5+5; int c[maxn], w[maxn], laz[maxn << 2]; ll n,mod=1e9+7; /*单点更新*/ void add(int x, int k) { while (x<=n) c[x]=(c[x]+k)%mod, x+=lowbit(x); } /*求w[1]~w[x]的和*/ ll getSum(int x) { ll ans=0; while (x>0) ans=(ans+c[x])%mod, x-=lowbit(x); return ans%mod; } /*求w[l]~w[r]的和*/ ll getsum(int l, int r, int s, int t, int p) { return (getSum(r)-getSum(l-1)+mod)%mod; } /*初始化*/ void init() { for(int i=1;i<=n;++i) add(i,w[i]); } 一些关于树状数组的技巧 /*O(n)建树*/ void init() { for(int i=1;i<=n;++i) { c[i]+=w[i]; int j=i+lowbit(i); if(j<=n) c[j]+=c[i]; } }

树状数组彻底入门,算法小白都看得懂的超详细解析(转载)

为君一笑 提交于 2019-12-06 02:25:05
转载自 https://blog.csdn.net/Small_Orange_glory/article/details/81290634 树状数组,重点是在 树状 的数组 大家都知道二叉树吧 叶子结点代表A数组A[1]~A[8] ....... 现在变形一下 现在定义每一列的顶端结点C[]数组 如下图 C[i]代表 子树的叶子结点的权值之和//这里以求和举例 如图可以知道 C[1]=A[1]; C[2]=A[1]+A[2]; C[3]=A[3]; C[4]=A[1]+A[2]+A[3]+A[4]; C[5]=A[5]; C[6]=A[5]+A[6]; C[7]=A[7]; C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]; 下面观察如下图 将C[]数组的结点序号转化为 二进制 1=(001) C[1]=A[1]; 2=(010) C[2]=A[1]+A[2]; 3=(011) C[3]=A[3]; 4=(100) C[4]=A[1]+A[2]+A[3]+A[4]; 5=(101) C[5]=A[5]; 6=(110) C[6]=A[5]+A[6]; 7=(111) C[7]=A[7]; 8=(1000) C[8]=A[1]+A[2]+A[3]+A[4]+A[5]+A[6]+A[7]+A[8]; 对照式子可以发现 C[i]=A[i-2^k+1

树状数组详解

一个人想着一个人 提交于 2019-12-05 12:17:41
树状数组详解 先来看几个问题吧。 1.什么是树状数组? 顾名思义,就是用数组来模拟树形结构呗。那么衍生出一个问题,为什么不直接建树?答案是没必要,因为树状数组能处理的问题就没必要建树。和Trie树的构造方式有类似之处。 2.树状数组可以解决什么问题 可以解决大部分基于区间上的更新以及求和问题。 3.树状数组和线段树的区别在哪里 树状数组可以解决的问题都可以用线段树解决,这两者的区别在哪里呢?树状数组的系数要少很多,就比如字符串模拟大数可以解决大数问题,也可以解决1+1的问题,但没人会在1+1的问题上用大数模拟。 4.树状数组的优点和缺点 修改和查询的复杂度都是O(logN),而且相比线段树系数要少很多,比传统数组要快,而且容易写。 缺点是遇到复杂的区间问题还是不能解决,功能还是有限。 一、树状数组介绍 二叉树大家一定都知道,如下图 如果每个父亲都存的是两个儿子的值,是不是就可以解决这类区间问题了呢。是的没错,但是这样的树形结构,叫做线段树。 那真的的树形结构是怎样的,和上图类似,但省去了一些节点,以达到用数组建树。 黑色数组代表原来的数组(下面用A[i]代替),红色结构代表我们的树状数组(下面用C[i]代替),发现没有,每个位置只有一个方框,令每个位置存的就是子节点的值的和,则有 C[1] = A[1]; C[2] = A[1] + A[2]; C[3] = A[3]; C[4]

hdu3015 树状数组

蓝咒 提交于 2019-12-05 01:03:10
有一天,索菲亚(Sophia)找到了一个很大的广场。广场上有n棵树。他们都很高。索菲娅对他们非常有趣。 她发现树可能不和谐,并且两棵树之间的不和谐值与两个值FAR和SHORT相关联。 FAR的定义如下:如果我们将所有这些树按照它们的X坐标按升序排列,则X坐标最小的树排名第一,X坐标相同的树排名相同。例如,如果有5棵树,它们的X坐标为3、3、1、3、4。那么他们的等级可能是2、2、1、2、5。X坐标为D1和D2的两棵树的FAR定义为F = abs(D1-D2)。 SHORT的定义类似于FAR。如果将所有这些树按照它们的高度升序排列,则高度最短的树排名第一。相同高度的树排名相同。例如,如果有5棵树的高度分别为4,1,9,7,4。那么他们的等级可能是2、1、5、4、2。高度等级为H1和H2的两棵树的SHORT定义为S = min(H1,H2)。 两棵树的不和谐值定义为F * S。因此,从上面的定义可以看出,如果两棵树的FAR较大,则不和谐值较大。Disharmony值也与两棵树中较短的一棵树相关。 现在为您提供每棵树的X坐标及其高度,请告诉Sophia所有树中每两棵树的Disharmony值的总和。 输入项 输入中有几个测试用例 对于每个测试用例,第一行包含一个整数N(2 <= N <= 100,000)N表示树的数量。 然后在N行之后,每行包含两个整数:X,H(0 <X,H <= 1

luogu P1908 逆序对 |树状数组

99封情书 提交于 2019-12-04 08:33:02
题目描述 猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。 Update:数据已加强。 输入格式 第一行,一个数n,表示序列中有n个数。 第二行n个数,表示给定的序列。序列中每个数字不超过10^9 输出格式 给定序列中逆序对的数目。 说明/提示 对于25%的数据,n≤2500 对于50%的数据,n≤4×10^4 对于所有数据,n≤5×10^5 请使用较快的输入输出 应该不会n方过50万吧 by chen_zhe #include<queue> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> const int N=5e5+10; #define int long long using namespace std; int c[N],n,a[N],b[N]; inline void add(int x,int y){ for(;x<=n;x+=x&(-x))c[x]+=y

洛谷 P3368 【模板】树状数组 2 题解

痴心易碎 提交于 2019-12-04 07:04:36
P3368 【模板】树状数组 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的值 输入格式 第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。 接下来M行每行包含2或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x 含义:输出第x个数的值 输出格式 输出包含若干行整数,即为所有操作2的结果。 输入输出样例 输入 #1 5 5 1 5 4 2 3 1 2 4 2 2 3 1 1 5 -1 1 3 5 7 2 4 输出 #1 6 10 说明/提示 时空限制:1000ms,128M 数据规模: 对于30%的数据:N<=8,M<=10 对于70%的数据:N<=10000,M<=10000 对于100%的数据:N<=500000,M<=500000 样例说明: 故输出结果为6、10 【思路】 树状数组 【题目大意】 区间修改和单点查询 【题目分析】 明显就是要用线段树的好吧 但是既然是树状数组的模板还是要给他个面子的 所以还得用树状数组写 区间修改和单点查询 第一瞬间想到的时候暴力枚举每一个区间内的点修改 然后求x位置的时候用sum(x) - sum(x - 1

树状数组的扩展

馋奶兔 提交于 2019-12-04 07:01:57
一、基础树状数组 (一)前缀和实现(单点修改,区间查询) inline int lowbit(int x){return x&-x;} inline void change(int x,int k){ while(x<=n){ tree[x]+=k; x+=lowbit(x); } } inline ll query(int x){ ll ans=0; while(x){ ans+=tree[x]; x-=lowbit(x); } return ans; } (二)差分实现(区间修改,单点查询) 区间修改,单点查询在修改时维护一个差分即可 change(x,k); change(y+1,-k); 二、区间树状数组(区间修改,区间查询) 考虑 \([1,r]\) 的区间和 令 \(tree[i]=a[i]-a[i-1]\) ,我们有 \(a[i]=\sum_\left.k=1\right.^i tree[k]\) 则 \[ \sum_\left.i=1\right.^ra[i]=\sum_\left.i=1\right.^r\sum_\left.k=1\right.^i tree[k]=\sum_\left.i=1\right.^\left.r\right.tree[i]*(r-i+1)=r*\sum_\left.i=1\right.^rtree[i]-\sum_\left.i=1

洛谷 P3374 【模板】树状数组 1 题解

拈花ヽ惹草 提交于 2019-12-04 06:58:33
P3374 【模板】树状数组 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入格式 第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。 接下来M行每行包含3个整数,表示一个操作,具体如下: 操作1: 格式:1 x k 含义:将第x个数加上k 操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和 输出格式 输出包含若干行整数,即为所有操作2的结果。 输入输出样例 输入 #1 5 5 1 5 4 2 3 1 1 3 2 2 5 1 3 -1 1 4 2 2 1 4 输出 #1 14 16 说明/提示 时空限制:1000ms,128M 数据规模: 对于30%的数据:N<=8,M<=10 对于70%的数据:N<=10000,M<=10000 对于100%的数据:N<=500000,M<=500000 样例说明: 故输出结果14、16 【思路】 之前做过现在是拿出来复习一下,没想到由于太久不写导致生疏到什么东西都忘掉了。。 只能重学树状数组 详细的讲解很多题解和博客都写得很好了, 所以我只在这里说一下易错或者难懂的地方 【题目大意】 单点修改,区间查询 【树状数组复杂度】 树状数组的查询和修改的复杂度都是最坏情况nlogn

奶牛抗议 DP 树状数组

老子叫甜甜 提交于 2019-12-04 06:53:28
奶牛抗议 DP 树状数组 USACO的题太猛了 容易想到 \(DP\) ,设 \(f[i]\) 表示为在第 \(i\) 位时方案数,转移方程: \[ f[i]=\sum f[j]\;(j< i,sum[i]-sum[j]\ge0) \] \(O(n^2)\) 过不了,考虑优化 移项得: \[ f[i]=\sum f[j]\;(j< i,sum[i]\ge sum[j]) \] 这时候我们发现相当于求在 \(i\) 前面并且前缀和小于 \(sum[i]\) 的所有和,这就可以用一个树状数组优化了,在树状数组维护下标为 \(sum[i]\) , \(f[i]\) 的前缀和。对于每个 \(f[i]\) 即为树状数组上 \(sum[i]\) 的前缀和。 这里需要注意的是前缀和可能为负,而树状数组下标不能为负,所以我们要离散化一下。 #include <cstdio> #include <algorithm> using namespace std; #define MAXN 100010 #define lowbit(x) ((x)&(-(x))) #define MOD 1000000009 int n,sum[MAXN],s; int sum_sort[MAXN+1]; int tre[MAXN+1]; inline void add(int x, int val){ while(x<

【算法#3】树状数组&二叉索引树

て烟熏妆下的殇ゞ 提交于 2019-12-04 04:06:50
其实是数据结构。 智推连续几天给我推树状数组的模板,还放在第一位…… 对着蓝书的图看了好几天才看懂,树状数组的另外一个名字是二叉索引树,指通过把一个数组抽象的变形成树状的以求得到树形数据结构的效果。有人说是线段树的阉割版,我不太清楚,树状数组应该是不支持区间修改加速的。 首先我们需要理解lowbit的概念,它指的是一个数转成二进制后位数最低的那个1表示的值。它具有一个特殊的性质但是为什么具有这个性质是无须证明也不用了解的。 然后我们画一个图,在一定范围内按lowbit的大小从上向下逐层分布,一层中按编号排序。不难发现这是棵二叉树。(见蓝书)我们让每个节点水平延伸lowbit(它的)-1长度的条,包括该节点在内的长条表示它覆盖的区间内的所有点的和或者其他什么性质。 并不一定非要纠结于这个图,然后我们发现lowbit的一个特殊性质就是一个区间的编号减去它的lowbit等于不包含这个区间的且在该区间前面的第一个(最靠右)的区间。一个区间的编号加上它的lowbit等于包含该区间的且最小(最靠左)的区间的编号。很玄学,对吧。 我们知道了lowbit的概念和它的性质,那么怎么求lowbit呢?我们知道,计算机中采用补码,也就是取一个数的负数等于这个数取反加一,以100为例: 01100100(100) 10011011(-99)//是-101还是-99? 10011100(-100)