树状数组

树状数组及二维树状数组

末鹿安然 提交于 2020-01-08 19:28:35
一直以为树状数组能用线段树水过去,直到我今天碰上了树状数组模板题。 然后就是开始认真的学习树状数组,突然发现怎么这么好写qwqqqq。 部分图片转自 https://www.cnblogs.com/hsd-/p/6139376.html 一.树状数组 树状数组是一种数据结构,核心思想是利用二进制的补码思想。 首先就是树状数组的结构图 然后我们对他进行变形 是不是感觉更好理解了呢? 然后我们对其进行标号 c数组表示的是记录的值,A数组表示的是原序列。然后就是所有关于树状数组的博客都会有的c数组的存值的展示,博主较懒就不写了,具体看推荐博客。 最重要的性质是 C[i]=A[i-2^k+1]+A[i-2^k+2]+......A[i]; (k为i的二进制中从最低位到高位连续零的长度) 上面说k是二进制中最低位到最高位的连续零的长度也就是6的二进制是110,那么k就是1,然后我们带入: c[6]=A[6-2+1]+A[6-2+2]=A[5]+A[6] 看,是不是与上方式子相符,这就是 lowbit 要实现的功能。那么, lowbit 实现的原理是啥呢? 先粘一下 lowbit 函数的代码 1 int lowbit(int k) 2 { 3 return k&(-k); 4 } lowbit k&(-k)是啥意思呢? -k是k的补码,也就是反码+1,反码是啥自行百度,反正也很简单。

[线段树][Splay][树状数组]JZOJ 3292 发牌

我怕爱的太早我们不能终老 提交于 2020-01-08 16:17:00
Description 在一些扑克游戏里,如德州扑克,发牌是有讲究的。一般称呼专业的发牌手为荷官。荷官在发牌前,先要销牌(burn card)。所谓销牌,就是把当前在牌库顶的那一张牌移动到牌库底,它用来防止玩家猜牌而影响游戏。 假设一开始,荷官拿出了一副新牌,这副牌有N张不同的牌,编号依次为1到N。由于是新牌,所以牌是按照顺序排好的,从牌库顶开始,依次为1, 2,……直到N,N号牌在牌库底。为了发完所有的牌,荷官会进行N次发牌操作,在第i次发牌之前,他会连续进行Ri次销牌操作,Ri由输入给定。请问最后玩家拿到这副牌的顺序是什么样的? 举个例子,假设N = 4,则一开始的时候,牌库中牌的构成顺序为{1, 2, 3, 4}。 假设R1=2,则荷官应该连销两次牌,将1和2放入牌库底,再将3发给玩家。目前牌库中的牌顺序为{4, 1, 2}。 假设R2=0,荷官不需要销牌,直接将4发给玩家,目前牌库中的牌顺序为{1,2}。 假设R3=3,则荷官依次销去了1, 2, 1,再将2发给了玩家。目前牌库仅剩下一张牌1。 假设R4=2,荷官在重复销去两次1之后,还是将1发给了玩家,这是因为1是牌库中唯一的一张牌。 Input 第 1行,一个整数 N,表示牌的数量。 第 2行到第 N + 1行,在第 i + 1行,有一个整数Ri, 0≤Ri<N Output 第 1行到第N行:第 i行只有一个整数

POJ 2299树状数组求逆序对

风流意气都作罢 提交于 2020-01-08 13:06:52
求逆序对最常用的方法就是树状数组了,确实,树状数组是非常优秀的一种算法。在做POJ2299时,接触到了这个算法,理解起来还是有一定难度的,那么下面我就总结一下思路: 首先:因为题目中a[i]可以到999,999,999之多,在运用树状数组操作的时候,用到的树状数组C[i]是建立在一个有点像位存储的数组的基础之上的,不是单纯的建立在输入数组之上。 比如输入一个9 1 0 5 4(最大9) 那么C[i]树状数组的建立是在: 下标 0 1 2 3 4 5 6 7 8 9 –——下标就要建立到9 数组 1 1 0 0 1 1 0 0 0 1 –——通过1来表示存在 现在由于999999999这个数字相对于500000这个数字来说是很大的,所以如果用数组位存储的话,那么需要999999999的空间来存储输入的数据。 这样是很浪费空间的,题目也是不允许的,所以这里想通过离散化操作。 那么怎么离散化操作呢?离散化是一种常用的技巧,有时数据范围太大,可以用来放缩到我们能处理的范围,必要的是建立一个结构体a[n],v表示输入的值,order表示原i值,再用一个数组aa[n]存储离散化后的值 例如: i:1 2 3 4 5 v:9 0 1 5 4 sort:0 1 4 5 9 order:2 3 5 4 1 aa:5 1 2 4 3 //建立映射:aa[a[i].order]=i;

完全认识树状数组

风格不统一 提交于 2020-01-08 08:56:00
我搜遍了网络,只在topcoder的网站上了解到树状数组这个结构是在设计压缩算法时被发现的。这个数据结构真是天才的构想,膜拜! 树状数组的基础是一个被构造出来的式子:C[i]=A[i]+A[i-1]+....+A[i-2^k+1];k代表i的二进制的最后连续0的个数 比如 对于1000和101000,k=3。至于这个式子是怎么被构造出来的,k为什么要代表这个。因为二进制的思想。 根据这个图来看节点与其子树的关系 接下来则很容易发现 节点和子节点的是有关系的,这种关系就是 i=j+lowbit(j); lowbit是j的最低位1所代表的数字 比如对于 1000(8的二进制) 1000=100+lowbit(100)=110+lowbit(110)=111+lowbit(111); 这个关系是树状数组的核心,有了这个关系,我们可以把子区间的变化以log2n的次数传递上去 那我们也知道了 当我们要求 1-n的和时,我们同样把n表示为2进制,我们知道 C[i]=A[i]+A[i-1]+....+A[i-2^k+1]; 所以对于i 是不是我们只要把他所有的1都用上 就可以表示1-n的和? 举个例子 求1-11000 则 Sum(11000)=C[11000]+C[10000]; 因为 根据C[i]的构造方法 C[i]是从A[i]开始的2^k个元素的和,则C[11000]求了A[11000]

浅谈树状数组求逆序对

六月ゝ 毕业季﹏ 提交于 2020-01-08 07:25:12
做了一道树上求逆序对的题,主要难点并不在于树形结构,而是求逆序对数。(在我看来是这样的)。 to洛谷P3605晋升者计数。 发现自己树状数组求逆序对还有个坑,先填上再说。再加上最近学的树状数组离散化,捋一捋思路。 首先是离散化 for(int i=1;i<=n;i++){ a[i].v=read(); a[i].id=i; } sort(a+1,a+1+n);按v排序 for(int i=1;i<=n;i++)b[a[i].id]=i; 在上述代码中,首先我们输入的是a[i].v,也就是一开始的数据,我们将其放到结构体里,再记录一下id,也就是原序。之后我们将a按照v排序,那么得到的就是从小到大的顺序。 又因为离散化是为了避免数据太大而出锅,所以我们在乎的就是数据的相对顺序,所以很容易地想到,将他们的顺序作为新的权值,这样就可以保证相对大小不变并且还可以最大化的缩小数据范围。 那么我们开一个新的数组,对应为离散化之后的数组,那么这个数组一定是要和原数组的顺序相同的,所以这个时候原来记录的id也就是原序就有了用场,然后再把当前这个元素的排名也就是i赋给离散化数组的第a[i].id位即可。 再来说逆序对数,在一个序列中,如果存在 \[ i<j并且a[i]>a[j] \] 那么就代表有一个逆序对。 考虑如何用树状数组实现 \(nlogn\) 的时间复杂度求逆序对数。 首先

树状数组的原理和实现

爷,独闯天下 提交于 2020-01-08 05:45:44
树状数组的原理和实现 概念 树状数组或者二叉索引树也称作Binary Indexed Tree,又叫做Fenwick树;它的查询和修改的时间复杂度都是 log(n) ,空间复杂度则为 O(n) ,这是因为树状数组通过将线性结构转化成树状结构,从而进行跳跃式扫描。通常使用在高效的计算数列的前缀和,区间和。 其中a数组就是原数组,c数组则是树状数组,可以发现 C1 = A1 C2 = A1+A2 C3 = A3 C4 = A1+A2+A3+A4 C5 = A5 C6 = A5+A6 C7 = A7 C8 = A1+A2+A3+A4+A5+A6+A7+A8 原理 lowbit 它通过公式来得出k,其中k就是该值从末尾开始0的个数。然后将其得出的结果加上x自身就可以得出当前节点的父亲节点的位置或者是x减去其结果就可以得出上一个父亲节点的位置。比如当前是6,二进制就是0110,k为2,那么6+2=8,而C(8)则是C(6)的父亲节点的位置;相反,6-2=4,则是C(6)的上一个父亲节点的位置。 def LOWBIT(x): return x & (-x) 注意:LOWBIT无法处理0的情况,因为它的结果也是0,那么最终就是一个死循环 单点修改 当我们要对最底层的值进行更新时,那么它相应的父亲节点存储的和也需要进行更新,所以 修改 的代码如下: def MODIFY(x, delta): if

树状数组 --- (离散化+树状数组、求逆序对)

陌路散爱 提交于 2020-01-08 05:04:13
G.Inversions There are N integers (1<=N<=65537) A1, A2,.. AN (0<=Ai<=10^9). You need to find amount of such pairs (i, j) that 1<=i<j<=N and A[i]>A[j]. Input The first line of the input contains the number N. The second line contains N numbers A1...AN. Output Write amount of such pairs. Sample Input Sample test(s) Input 5 2 3 1 5 4 Output 3 【题目来源】:BUN 【题目大意】:求逆序对的个数 【题目分析】 求逆序对有很多方法,比如说用合并排序、分治、树状数组、线段树,甚至连暴力(冒泡排序)也可以做,但是要注意会不会超时。 这里就讲一下树状数组的方法,这一题最有意思的是离散化的方法,这个方法在处理大数据的排序方面很有用,离散化能够有效的降低时空复杂度,他可以改进一个低效的算法。除了加上了一个离散化,其他的用树状数组就可以解决。 1、什么是离散化? 用我的理解来说就是一种映射,为什么能用离散化呢?或者说离散化能用在哪些方面呢? 举个例子说吧 ,在排序

pku2352--stars---树状数组

我怕爱的太早我们不能终老 提交于 2020-01-08 01:25:49
题目简单介绍: 天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标。 如果一个星星的左下方(包含正左和正下)有k颗星星,就说这颗星星是k级的。 求出各个级别的星星的个数。 首先,引入树状数组,文章摘自 http://fqq11679.blog.hexun.com/21722866_d.html 【引言】 在解题过程中,我们有时需要维护一个数组的前缀和S[i]=A[1]+A[2]+...+A[i]。 但是不难发现,如果我们修改了任意一个A[i],S[i]、S[i+1]...S[n]都会发生变化。 可以说,每次修改A[i]后,调整前缀和S[]在最坏情况下会需要O(n)的时间。 当n非常大时,程序会运行得非常缓慢。 因此,这里我们引入“树状数组”,它的修改与求和都是O(logn)的,效率非常高。 【理论】 为了对树状数组有个形 象的认识,我们先看下面这张图。 如图 所示,红色矩形表示的数组C[]就是树状数组。 这里,C[i]表示A[i-2^k+1]到A[i]的和,而k则是i在二进制时末尾0的个数, 或者说是i用2的幂方和表示时的最小指数。 ( 当然,利用位运算,我们可以直接计算出2^k=i&(i^(i-1)) ) 同时,我们也不难发现,这个k就是该节点在树中的高度,因而这个树的高度不会超过logn。 所以,当我们修改A[i]的值时,可以从C[i]往根节点一路上溯

poj 1990 树状数组

喜欢而已 提交于 2020-01-05 04:40:52
传送门: https://vjudge.net/problem/POJ-1990 题意:m头牛,每头牛有两个值v和x。然后每两头牛之间的值是abs(x1-x2) * Max(v1,v2)。问所有m*(m-1)/2对牛之间值的总和。 白书上来的。就是用树状数组做。首先肯定是按照v排序,这样就可以不用管v了。接下来我们看看x。 我们先对所有牛的x排序,然后每头牛有一个idx代表这头牛的x在所有牛的x中排第几位。然后有两个树状数组,num[N]和dis[N]。num[i]表示比i小的有多少个,dis[i]表示比i小的x的和(这里的i指在X[N]数组排名第i位)。然后我按照v从小到大的顺序枚举每一头牛,每次进行维护,这样枚举到的牛所查询树状数组里的都是v比它小的。这样这头牛对答案的贡献就是由两部分构成。 第一部分x比它小的 就是比v×(x-xi)的和,整理就是v×(cnt×x-dis[i]), 然后我在记录目前为止总共加了多少个点进树状数组,以及这些点的x的总和totdis。 这样第二部分就是x比它大的就很好求了。 讲的可能不是很清楚,具体看代码吧。 1 // Cease to struggle and you cease to live 2 #include <iostream> 3 #include <cmath> 4 #include <cstdio> 5 #include

树状数组专题总结1

白昼怎懂夜的黑 提交于 2020-01-05 03:06:07
如果给定一个数组,要你求里面所有数的和,一般都会想到累加。但是当那个数组很大的时候,累加就显得太耗时了,时间复杂度为O(n),并且采用累加的方法还有一个局限,那就是,当修改掉数组中的元素后,仍然要你求数组中某段元素的和,就显得麻烦了。所以我们就要用到树状数组,他的时间复杂度为O(lgn),相比之下就快得多。下面就讲一下什么是树状数组: 一般讲到树状数组都会少不了下面这个图: 下面来分析一下上面那个图看能得出什么规律: 据图可知:c1=a1,c2=a1+a2,c3=a3,c4=a1+a2+a3+a4,c5=a5,c6=a5+a6,c7=a7,c8=a1+a2+a3+a4+a5+a6+a7+a8,c9=a9,c10=a9+a10,c11=a11........c16=a1+a2+a3+a4+a5+.......+a16。 分析上面的几组式子可知,当 i 为奇数时,ci=ai ;当 i 为偶数时,就要看 i 的因子中最多有二的多少次幂,例如,6 的因子中有 2 的一次幂,等于 2 ,所以 c6=a5+a6(由六向前数两个数的和),4 的因子中有 2 的两次幂,等于 4 ,所以 c4=a1+a2+a3+a4(由四向前数四个数的和)。 (一)有公式:cn=a(n-a^k+1)+.........+an(其中 k 为 n 的二进制表示中从右往左数的 0 的个数)。 那么,如何求 a^k 呢