树状数组

树状数组求逆序数+离散化

前提是你 提交于 2019-11-27 02:39:53
逆序对指的是一个序列中有两个数ai和aj,i<j&&ai>aj,即它们下标与数值的增减不一致,那么对于这个问题:求一个序列中逆序对的个数,该如何解决呢? 树状数组求逆序的思想事实上和树状数组关系不大,以下图为例(自己画的,丑:): 如上图,第一次将第一个数1对应的a[1]++,这时还看不出来,再将4对应的a[4]++,同理a[2]++……即将n对应的a[n]++,这样做,就可以将一个无序的序列变得有序,同时a数组也表示了对应下标的数是否出现/处理过,而且当只有i个元素变换之时,剩余元素不会对接下来的操作造成影响,现在给出计算到2时的图片: 那么,对于最新处理的2或任意一个n(设下标为i)来说,前i个数中,比它小及其本身的数的个数,就是前i项的前缀和,因为排在原序列中i之后的数尚未处理,而已处理的a中比他小的数必然在它前面,且对应a[]值为1,那么,已处理的i个数中,比这个数n大的数的个数,也就是这一个数的逆序对数,就是i-getsum(n),而前缀和的求值与a数组的修改、维护,就可以交给树状数组了: 来源: https://blog.csdn.net/QGK_King/article/details/98486541

【模板】树状数组

廉价感情. 提交于 2019-11-27 01:32:22
树状数组适用范围:单点修改,区间修改,单点查询,区间查询 注: a[]:原数组 tree[]:前缀和数组 -lowbit int lowbit(int n) { return n&(-n); } : -单点修改: void add(int x,int k)//在a[x]加上k,相当于在tree[]中x~n区间加上k { while(x<=N) { tree[x]+=k; x+=lowbit(x); } } -区间修改: 注意:要用到差分数组,与原来的前缀和数组不一样 void add_part(int x,int y,int k)//在a[x]~a[y]加上k,相当于在tree[]中x~y区间加上k,操作为从x~n区间加上k,再从y+1~n区间加上-k { add(x,k); add(y+1,-k); } - 单点查询: int search(int x) //查询a[x]的值 { int ans=0; while(x!=0) { ans+=tree[x]; x-=lowbit(x); } return ans; } - 区间查询: int sum(int x,int y)//查询a[x]~a[y]的和,相当于c[y]-c[x-1] { return search(y)-search(x-1); } 来源: https://blog.csdn.net/weixin_45485187

楼兰图腾(树状数组)

倾然丶 夕夏残阳落幕 提交于 2019-11-26 23:40:10
题目: 题目描述 在完成了分配任务之后,西部314来到了楼兰古城的西部。 相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(‘V’),一个部落崇拜铁锹(‘∧’),他们分别用V和∧的形状来代表各自部落的图腾。 西部314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了N个点,经测量发现这N个点的水平位置和竖直位置是两两不同的。 西部314认为这幅壁画所包含的信息与这N个点的相对位置有关,因此不妨设坐标分别为(1,y1),(2,y2),…,(n,yn),其中y1~yn是1到n的一个排列。 西部314打算研究这幅壁画中包含着多少个图腾。 如果三个点(i,yi),(j,yj),(k,yk)满足1≤i[HTML_REMOVED]yj,yj<yk,则称这三个点构成V图腾; 如果三个点(i,yi),(j,yj),(k,yk)满足1≤i[HTML_REMOVED]yk,则称这三个点构成∧图腾; 西部314想知道,这n个点中两个部落图腾的数目。 因此,你需要编写一个程序来求出V的个数和∧的个数。 解题报告: 咱们只需要求解的就是每个点左右两侧分别高于它和小于它的数目,就可以求出该点可以够成的V和A的数目,最后累加一下就可以,但是由于树状数组只可以求解该前边的的数目,所以转换了一下思想,就是使用getsum(n)-getsum(a[i]-1),就可以实现求解后边的数目。

暴力三维树状数组求曼哈顿距离求最值——牛客多校第八场D

岁酱吖の 提交于 2019-11-26 23:18:21
涉及的知识点挺多,但是大多是套路 1.求曼哈顿距离的最值一般对所有情况进行讨论 2.三维树状数组用来求前缀最大值 /* 有一个三维坐标系(x,y,z),取值范围为[1,n],[1,m],[1,h],有两种操作 1.在三维坐标系上更新一个点(x1,y1,z1) 2.给定一个点(x2,y2,z2),问在坐标系上离该点Manhattan距离最短的点 即最小的 |x2-x1|+|y2-y1|+|z2-z1| 令 f=|x2-x1|+|y2-y1|+|z2-z1|,那么可以讨论去绝对值后f的八种情况 f0=(x2+y2+z2)-(x1+y1+z1),x2>=x1,y2>=y1,z2>=z1 f1=(x2+y2-z2)-(x1+y1-z1),x2>=x1,y2>=y1,x2<x1 ... 考虑如何求每种情况的最小值 由于 x2+y2+z2 的值是固定的,只需要求出最大的符合条件的 x1+y1+z1即可,发现 x1<=x2 && y1<=y2 && z1<=z2这个条件刚好可以用三维树状数组来维护(求前缀最大值,单点更新) 同理 八种情况都可以用八颗三维树状数组来维护 另外 考虑 f1的条件 需要将 x1>x2转换成 n-x1+1<=n-x2+1 然后更新的是x1+y1-z1,查询的结果是最大的 x1+y1-z1 其他情况同理 (由于n*m*h<=1e5,所以用一个三维转一维的方式来存储) */

树状数组基础

大城市里の小女人 提交于 2019-11-26 23:04:31
树状数组,顾名思义,是一个树形的数据结构,它的基本用途是较高效地维护序列的前缀和。 先补充几个知识: lowbit运算:取出非负整数n在二进制下最低位的1以及它后边的0构成的数值。例如,若n=6,则n在二进制下表示为110,所以lowbit(n)=2。如何实现lowbit运算呢?设n>0,n的第k位是1,第0~k-1位都是0,则: 将n取反,此时第k位为0,第0~k-1位为1; 将n自加,即n=n+1,由于进位,此时第k位为1,第0~k-1位为0; 将前两步操作后的数与上n。 在前两步操作过后,n的第k+1位至最高位都与原来相反,所以三步操作后的数,即n&(~n+1),仅有第k位为1,其余位都为0。而在补码表示下,~n=-1-n,因此可以得出lowbit运算的公式: lowbit(n)=n&(-n) 由于树状数组比较抽象,在讲解之前,先放一张概念图: 其中,c[x]存储区间[x-lowbit(x)+1,x]中所有数的和。 树状数组有以下几个性质: 每个内部节点c[x]存储以它为根的子树中所有叶节点的和 每个内部节点c[x]的子节点个数等于lowbit(x)的位数 除树根外,每个内部节点c[x]的父节点为c[x+lowbit(x)] 树的深度为O(logN) 正因为树状数组满足以上的性质,它才能高效地维护序列的前缀和。在实现过程中,我们只要建立一个树状数组c[N]

P3368 【模板】树状数组 2

帅比萌擦擦* 提交于 2019-11-26 21:18:52
题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数数加上x 2.求出某一个数的值 输入格式: 第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。 第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。 接下来M行每行包含2或4个整数,表示一个操作,具体如下: 操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k 操作2: 格式:2 x 含义:输出第x个数的值 输出格式: 输出包含若干行整数,即为所有操作2的结果。 输入样例 5 5 1 5 4 2 3 1 2 4 2 2 3 1 1 5 -1 1 3 5 7 2 4 输出样例 6 10 说明 时空限制:1000ms,128M 数据规模: 对于30%的数据:N<=8,M<=10 对于70%的数据:N<=10000,M<=10000 对于100%的数据:N<=500000,M<=500000 样例说明: 故输出结果为6、1 思路: 树状数组模板题!!! 代码: #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=500010; long long n,q,flag,x,y

P3374 【模板】树状数组 1

允我心安 提交于 2019-11-26 21:18:46
题目描述 如题,已知一个数列,你需要进行下面两种操作: 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 样例说明: 故输出结果14、16 思路: 树状数组模板题!!! 代码: #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=500010; int tree[N]; int n,m

树状数组求逆序数讲解

主宰稳场 提交于 2019-11-26 19:56:06
树状数组求逆序对 树状数组可以用nlogn的算法做到求出逆序对.但这里着重讲树状数组的原理与求法. 树状数组最常用的方面就是用来求逆序对, 普通方法需要n^2的复杂度, 而树状数组只需要用nlogn的复杂度, 所以是很好的优化, 关键在于内部函数lowbit的应用. 下面是树状数组的结构图 : 当数据的范围较小时,比如maxn=100000,那么我们可以开一个数组c[maxn],来记录前面数据的出现情况,初始化为0;当数据a出现时,就令c[a]=1。 这样的话,欲求某个数a的逆序数,只需要算出在当前状态下c[a+1,maxn]中有多少个1,因为这些位置的数在a之前出现且比a大。 但是若每添加一个数据a时,就得从a+1到 maxn搜一遍,复杂度太高了。 树状数组却能很好的解决这个问题,可以把数一个个插入到树状数组中, 每插入一个数, 统计比他小的数的个数,对应的逆序为 i - getsum( c[i] ),其中 i 为当前已经插入的数的个数, getsum( c[i] )为比 c[i] 小的数的个数,i- getsum( c[i] ) 即比c[i] 大的个数, 即逆序的个数。最后需要把所有逆序数求和,就是在插入的过程中边插入边求和. 举个例子:有5个数,分别为5 3 4 2 1,当读入数据a=5时,c为:0,0,0,0,1;d为:0,0,0,0,1;当读入数据a=3时,c为:0,0

树状数组

十年热恋 提交于 2019-11-26 19:33:40
详细介绍: https://www.cnblogs.com/xenny/p/9739600.html http://www.sohu.com/a/245746824_100201031 https://www.cnblogs.com/wkfvawl/p/9445376.html 单点更新、单点查询 传统数组可做 1.单点修改+区间查询 int n;//点的数量 int a[50005],c[50005]; //对应原数组和树状数组 int lowbit(int x){ return x&(-x); } void updata(int i,int k){ //在i位置加上k while(i <= n){ c[i] += k; i += lowbit(i); } } //求sum(x)就是a[x]的前缀和,想查询l~r区间的元素和只需要求出来sum(r)-sum(l-1) int getsum(int i){ int res = 0; while(i > 0){ res += c[i]; i -= lowbit(i); } return res; }int main(){ memset(a,0,sizeof(a)); memset(c,0,sizeof(c)); } 模板题: #include<iostream> #include<cstdio> #include<cmath>

bzoj_1036 树状数组套线段树

♀尐吖头ヾ 提交于 2019-11-26 16:02:28
bzoj_1036 ★★★★ 输入文件:bzoj_1036.in 输出文件:bzoj_1036.out 简单对比 时间限制:1 s 内存限制:162 MB 【 题 目描述】 一棵树上有 n 个节点,编号分别为 1 到 n ,每个节点都有一个权值 w 。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点 u 的权值改为 t II. QMAX u v: 询问从点 u 到点 v 的路径上的节点的最大权值 III. QSUM u v: 询问从点 u 到点 v 的路径上的节点的权值和 注意:从点 u 到点 v 的路径上的节点包括 u 和 v 本身 【 输 入格式】 输入的第一行为一个整数 n ,表示节点的个数。 接下来 n – 1 行,每行 2 个整数 a 和 b ,表示节点 a 和节点 b 之间有一条边相连。 接下来 n 行,每行一个整数,第 i 行的整数 wi 表示节点 i 的权值。 接下来 1 行,为一个整数 q ,表示操作的总数。 接下来 q 行,每行一个操作,以 “CHANGE u t” 或者 “QMAX u v” 或者 “QSUM u v” 的形式给出。 对于 100 %的数据,保证 1<=n<=30000 , 0<=q<=200000 ;中途操作中保证每个节点的权值 w 在 -30000 到 30000 之间。 【 输 出格式】 对于每个