鉴于我已经不会写树状数组[捂脸],新开一坑QAQAQ
树状数组支持
查询/修改区间最值,查询/修改区间和,单点修改
lowbit(a)=aand(−a)
定义 C[i]=A[i−lowbit(i)+1]+...+A[i]
讲到树状数组必有的一张图
我们可以发现对于任意一个 C[i] ,如果修改了的话,会影响到的是 C[i+lowbit(i)] ,所以每次向上依次修改即可,(写成递归形式)
查询 [L,R] 的 sum 时,变成 sum[1,R]−sum[1,R]
单点修改时,修改c[i]需要比较的是 c[i−20],c[i−21]……c[i−lowbit(i)+1]
区间最值查询时,对于一个区间[L,R],如果R-lowbit(R)+1在[L,R]内那么取c[R-lowbit(R)+1,R]比较,如果不在,那么R变R-1
[BZOJ 1012] [JSOI2008] 最大数maxnumber
我们用差分序列来维护区间区间修改后的单点修改,因为差分序列序需要前缀和来单点查询,树状数组的区间查询正好就是靠前缀和来实现的,所以树状数组装的值就是差分的值
令 d[i]=a[i]−a[i−1]
单点查询 a[i]=∑ij=1d[j]
用树状数组维护 d[i] 这个序列即可
[BZOJ1782] [Usaco2010 Feb]slowdown 慢慢游
有上面单点查询的基础我们再来看区间查询
∑ni=1a[i]=∑ni=1∑ij=1d[j]=∑ni=1d[i]∗(n−i+1)=(n+1)∗∑ni=1d[i]−∑ni=1d[i]∗i
(注意理解一下上面的推导)
所以我们维护 d[i]和i∗d[i] 两棵树状数组即可
一维的我们这么写
void update(int x,int val)
{
for(int i=x;i<=n;i+=lowbit(i))
bit[i]+=val;
}
二维的就
void update(int x,int y,int c,int val)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
bit[i][j]+=val;
}
很简单吧
我们定义对 d[i,j]+val 为对原矩阵 (i,j)−(n,m) 整体 +val
其中 n,m 为二维边界
例如修改矩阵为 (x1,y1)−(x2,y2) ,那么我们执行
update(x1,y1,val)
update(x2+1,y1,−val)
update(x1,y2+1,−val)
update(x2+1,y2+1,val)
这四步即可
a[i,j]=∑ik=1∑jl=1d[k,l]
所以差分也可以理解为区间转化为点对它后面的贡献
∑xi=1∑yj=1a[i,j]=∑xi=1∑yj=1∑ik=1∑jl=1d[k,l]=∑xi=1∑yj=1d[i,j]∗(x−i+1)∗(y−j+1)
有一维的基础就很好理解了吧
然后我们把它展开QAQAQ
∑xi=1∑yj=1d[i,j]∗(x−i+1)∗(y−j+1)=(x+1)∗(y+1)∗∑xi=1∑yj=1d[i,j]−(x+1)∗∑xi=1∑yj=1d[i,j]∗j−(y+1)∗∑xi=1∑yj=1d[i,j]∗i+∑xi=1∑yj=1d[i,j]∗i∗j
所以我们维护四棵树状数组 d[i,j],d[i,j]∗i,d[i,j]∗j,d[i,j]∗i∗j 即可
切记 树状数组下标要从1开始,因为lowbit(0)=0,就进入死循环了
每次各种操作的复杂度都是 O(logn)
这个其实很简单,所以基本的YY一下就好了
procedure pushdown(a:longint);
begin
if x[a,1]=x[a,2] then begin x[a,4]:=0; exit; end;
inc(x[a*2,3],x[a,4]); inc(x[a*2,4],x[a,4]);
inc(x[a*2+1,3],x[a,4]); inc(x[a*2+1,4],x[a,4]);
x[a,4]:=0;
end;
主席树可以用来维护静态/动态询问区间第k大
%%%CLJ《可持久化数据结构研究》
我们对于所有数字离散化后建立权值线段树,维护离散化后大小点值在[L,R]内的数的个数,查询时,如果[L,mid]中的数的个数小于k,那么查询[mid+1,R],反之亦然
不支持修改,依次加入第i个数,加入一个数是以新建立一棵线段树的方式进行的,就是对于第i-1个数建立的线段树中包含第i个数的区间内+1,但如果全部新开节点的话会达到 O(N2) 级别,我们发现,这其中我们只改了一条链,即 logN 个节点的值,所以只要对这 logN 个节点新开即可,其他的节点都仍指向第i-1的数的节点
当询问[L,R] (注意这里的L,R不是权值)内的第k大时,我们从第L-1个数的根和第R个数的根同时向下走,那么区间内的数的个数就是R的权值-(L-1)的权值,根据前面提到的判断k大的方法即可
用树状数组维护前缀和即可,但空间上存在常数优化的问题,在此不做展开
与主席树类似,都是依靠前缀和相减来取出区间,与Trie一样一般是异或问题
移步我的另一篇文章Splay总结
https://oi.abcdabcd987.com/summary-of-link-cut-tree/
离线的基本思路就是:对于所有答案先左端点排序,然后处理处左端点的所有答案,然后考虑左端点向右移一位会带来什么影响
参考 许昊然《数据结构漫谈》
DFS序和树链剖分都是将树上的问题转化成序列上的问题的有效的方法
DFS序的性质:可以将某个点的子树转化为连续的一段
树链剖分