数据结构总结

鉴于我已经不会写树状数组[捂脸],新开一坑QAQAQ
##树状数组
树状数组支持

  • 单点修改+区间和查询
  • 单点修改+区间最值查询
  • 区间加减+单点查询

查询/修改区间最值,查询/修改区间和,单点修改
l o w b i t ( a ) = a a n d ( − a ) lowbit(a)=a and (-a) lowbit(a)=aand(a)
定义 C [ i ] = A [ i − l o w b i t ( i ) + 1 ] + . . . + A [ i ] C[i]=A[i-lowbit(i)+1]+...+A[i] C[i]=A[ilowbit(i)+1]+...+A[i]
讲到树状数组必有的一张图
这里写图片描述
我们可以发现对于任意一个 C [ i ] C[i] C[i],如果修改了的话,会影响到的是 C [ i + l o w b i t ( i ) ] C[i+lowbit(i)] C[i+lowbit(i)],所以每次向上依次修改即可,(写成递归形式)
查询 [ L , R ] [L,R] [L,R] s u m sum sum时,变成 s u m [ 1 , R ] − s u m [ 1 , R ] sum[1,R]-sum[1,R] sum[1,R]sum[1,R]
###一维单点修改+区间最值查询
单点修改时,修改c[i]需要比较的是 c [ i − 2 0 ] , c [ i − 2 1 ] … … c [ i − l o w b i t ( i ) + 1 ] c[i-2^0],c[i-2^1]……c[i-lowbit(i)+1] c[i20],c[i21]c[ilowbit(i)+1]
区间最值查询时,对于一个区间[L,R],如果R-lowbit®+1在[L,R]内那么取c[R-lowbit®+1,R]比较,如果不在,那么R变R-1
[BZOJ 1012] [JSOI2008] 最大数maxnumber
###一维区间加减+单点查询
我们用差分序列来维护区间区间修改后的单点修改,因为差分序列序需要前缀和来单点查询,树状数组的区间查询正好就是靠前缀和来实现的,所以树状数组装的值就是差分的值
d [ i ] = a [ i ] − a [ i − 1 ] d[i]=a[i]-a[i-1] d[i]=a[i]a[i1]
单点查询 a [ i ] = ∑ j = 1 i d [ j ] a[i]=\sum_{j=1}^id[j] a[i]=j=1id[j]
用树状数组维护 d [ i ] d[i] d[i]这个序列即可
[BZOJ1782] [Usaco2010 Feb]slowdown 慢慢游
###一维区间加减+区间查询
有上面单点查询的基础我们再来看区间查询
∑ i = 1 n a [ i ] = ∑ i = 1 n ∑ j = 1 i d [ j ] = ∑ i = 1 n d [ i ] ∗ ( n − i + 1 ) = ( n + 1 ) ∗ ∑ i = 1 n d [ i ] − ∑ i = 1 n d [ i ] ∗ i \sum_{i=1}^na[i]=\sum_{i=1}^n\sum_{j=1}^id[j]=\sum_{i=1}^nd[i]*(n-i+1)\\=(n+1)*\sum_{i=1}^nd[i]-\sum_{i=1}^nd[i]*i i=1na[i]=i=1nj=1id[j]=i=1nd[i](ni+1)=(n+1)i=1nd[i]i=1nd[i]i
(注意理解一下上面的推导)
所以我们维护 d [ i ] 和 i ∗ d [ i ] d[i]和i*d[i] d[i]id[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 ] + v a l d[i,j]+val d[i,j]+val为对原矩阵 ( i , j ) − ( n , m ) (i,j)-(n,m) (i,j)(n,m)整体 + v a l +val +val
其中 n , m n,m n,m为二维边界
例如修改矩阵为 ( x 1 , y 1 ) − ( x 2 , y 2 ) (x_1,y_1)-(x_2,y_2) (x1,y1)(x2,y2),那么我们执行
u p d a t e ( x 1 , y 1 , v a l ) update(x_1,y_1,val) update(x1,y1,val)
u p d a t e ( x 2 + 1 , y 1 , − v a l ) update(x_2+1,y_1,-val) update(x2+1,y1,val)
u p d a t e ( x 1 , y 2 + 1 , − v a l ) update(x_1,y_2+1,-val) update(x1,y2+1,val)
u p d a t e ( x 2 + 1 , y 2 + 1 , v a l ) update(x_2+1,y_2+1,val) update(x2+1,y2+1,val)
这四步即可
a [ i , j ] = ∑ k = 1 i ∑ l = 1 j d [ k , l ] a[i,j]=\sum_{k=1}^i\sum_{l=1}^jd[k,l] a[i,j]=k=1il=1jd[k,l]
所以差分也可以理解为区间转化为点对它后面的贡献
###二维矩阵加减+矩阵查询
     ∑ i = 1 x ∑ j = 1 y a [ i , j ] = ∑ i = 1 x ∑ j = 1 y ∑ k = 1 i ∑ l = 1 j d [ k , l ] = ∑ i = 1 x ∑ j = 1 y d [ i , j ] ∗ ( x − i + 1 ) ∗ ( y − j + 1 ) ~~~~\sum_{i=1}^x\sum_{j=1}^ya[i,j]\\=\sum_{i=1}^x\sum_{j=1}^y\sum_{k=1}^i\sum_{l=1}^jd[k,l]\\=\sum_{i=1}^x\sum_{j=1}^yd[i,j]*(x-i+1)*(y-j+1)     i=1xj=1ya[i,j]=i=1xj=1yk=1il=1jd[k,l]=i=1xj=1yd[i,j](xi+1)(yj+1)
有一维的基础就很好理解了吧
然后我们把它展开QAQAQ
     ∑ i = 1 x ∑ j = 1 y d [ i , j ] ∗ ( x − i + 1 ) ∗ ( y − j + 1 ) = ( x + 1 ) ∗ ( y + 1 ) ∗ ∑ i = 1 x ∑ j = 1 y d [ i , j ] − ( x + 1 ) ∗ ∑ i = 1 x ∑ j = 1 y d [ i , j ] ∗ j − ( y + 1 ) ∗ ∑ i = 1 x ∑ j = 1 y d [ i , j ] ∗ i + ∑ i = 1 x ∑ j = 1 y d [ i , j ] ∗ i ∗ j ~~~~\sum_{i=1}^x\sum_{j=1}^yd[i,j]*(x-i+1)*(y-j+1)\\=(x+1)*(y+1)*\sum_{i=1}^x\sum_{j=1}^yd[i,j]-(x+1)*\sum_{i=1}^x\sum_{j=1}^yd[i,j]*j-(y+1)*\sum_{i=1}^x\sum_{j=1}^yd[i,j]*i+\sum_{i=1}^x\sum_{j=1}^yd[i,j]*i*j     i=1xj=1yd[i,j](xi+1)(yj+1)=(x+1)(y+1)i=1xj=1yd[i,j](x+1)i=1xj=1yd[i,j]j(y+1)i=1xj=1yd[i,j]i+i=1xj=1yd[i,j]ij
所以我们维护四棵树状数组 d [ i , j ] , d [ i , j ] ∗ i , d [ i , j ] ∗ j , d [ i , j ] ∗ i ∗ j d[i,j],d[i,j]*i,d[i,j]*j,d[i,j]*i*j d[i,j],d[i,j]i,d[i,j]j,d[i,j]ij即可
切记 树状数组下标要从1开始,因为lowbit(0)=0,就进入死循环了
每次各种操作的复杂度都是 O ( l o g n ) O(logn) 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《可持久化数据结构研究》
###关于第k大
我们对于所有数字离散化后建立权值线段树,维护离散化后大小点值在[L,R]内的数的个数,查询时,如果[L,mid]中的数的个数小于k,那么查询[mid+1,R],反之亦然
###静态区间第k大
不支持修改,依次加入第i个数,加入一个数是以新建立一棵线段树的方式进行的,就是对于第i-1个数建立的线段树中包含第i个数的区间内+1,但如果全部新开节点的话会达到 O ( N 2 ) O(N^2) O(N2)级别,我们发现,这其中我们只改了一条链,即 l o g N logN logN个节点的值,所以只要对这 l o g N logN logN个节点新开即可,其他的节点都仍指向第i-1的数的节点
当询问[L,R] (注意这里的L,R不是权值)内的第k大时,我们从第L-1个数的根和第R个数的根同时向下走,那么区间内的数的个数就是R的权值-(L-1)的权值,根据前面提到的判断k大的方法即可
###动态区间第k大
用树状数组维护前缀和即可,但空间上存在常数优化的问题,在此不做展开
##可持久化Trie树
与主席树类似,都是依靠前缀和相减来取出区间,与Trie一样一般是异或问题
##平衡树
###Splay
移步我的另一篇文章Splay总结
##动态树
https://oi.abcdabcd987.com/summary-of-link-cut-tree/
##离线相关
离线的基本思路就是:对于所有答案先左端点排序,然后处理处左端点的所有答案,然后考虑左端点向右移一位会带来什么影响

  • [BZOJ3339] Rmq Problem&&[BZOJ3585] mex

  • [BZOJ3747] [POI2015]Kinoman

  • [BZOJ1878] [SDOI2009]HH的项链

  • [BZOJ2743] [HEOI2012]采花
    ##树上的数据结构问题
    参考 许昊然《数据结构漫谈》
    DFS序和树链剖分都是将树上的问题转化成序列上的问题的有效的方法
    DFS序的性质:可以将某个点的子树转化为连续的一段
    树链剖分

  • 单点修改+单点查询
    直接线段树/树状数组搞就行

  • 单点修改+子树查询
    dfs序让子树查询转为区间查询,线段树/树状数组

  • 单点修改+树链查询
    树链剖分

  • 子树加减+单点查询
    dfs序让子树修改转为区间修改,线段树/树状数组

  • 子树加减+子树查询
    dfs序,区间修改区间查询

  • 子树加减,路径查询
    树链剖分

  • 路径修改,单点查询
    树链剖分

  • 路径修改,子树查询
    树链剖分

  • 路径修改,单点查询

  • 这个就是树链剖分的裸题

  • 但是我不会树链剖分QAQAQ(upd:会啦~~~)

  • 把差分序列用在树上,要求点到根的和的时候,如果树的高度能维持在O(logN)的话就可以从根一路走下来了,但是显然会被卡,所以我们再维护一棵线段树,记录的权值为点到根的路径和,修改一点时他的子树所有点会同时改变相同值,这样就可以对于修改(a,b)的路径我们修改lca(a,b),son[a],son[b]的子树
    upd:son[a],son[b]的数量…那么也会被卡…

  • upd:以上都是口胡QAQAQ树上差分什么的才不可能实现的呢

  • 以下是正解,
    我们对于(a,b)的路径+w,相当于对于a到根+w,b到根+w,lca(a,b)到根-w,fa[lca(a,b)]到根-w
    所以对于a,b同时打上+w的标记,对于lca(a,b)和fa[lca(a,b)]同时打上-w的标记,单点查询时只要查询某点的子树标记和即可

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值