树分块 学习笔记
树分块有两种方法:
1."王室联邦"分块:
一个点可以在多个块中。
每个块有一个顶部,每个块只能和其它块共用顶部。
一个块的顶部是它的所有点的lca,顶部深度最小。
每个块的深度次小的节点的父亲相同,都是块顶。
对于每个块,如果插入每个块的顶部,则块内每个节点必须连通。
删除所有块的顶部,最多只有1个块的大小不在范围\([B,3B]\)之间。
分块方法:用栈和dfs,在dfs的过程中维护数组sz,表示当前和点\(x\)连通的,还未被分块的节点个数。
栈是未被分块节点集合。
当\(sz\geq B\)时,把栈中所有节点删除,给这些节点新分配一个块。
事实上,这样子分块后每个块也能构成树结构:把每个块新建一个点,每个块的顶部也建一个节点。
每个块顶和每个块对应的节点连边。
如果\(a\)块和\(b\)块有公共点但是块顶不同,则连边。
块顶为\(x\)。
2.随机分块
在树上随机取\(B\)个关键点。
把关键点和根建立虚树。
虚树上的点就是关键点。
这个分块的性质是:一个点期望向父亲跳\(\frac{n}{B}\)次就能遇到关键点。
它在处理路径信息比较方便。
随机分块:
问题1:树上路径众数。
先把询问的\((x,y)\)向跳到上面第一个关键点\((x',y')\)
如果\(x'=y'\),则可以暴力跳跃。
否则考虑\((x',y')\)路径上的众数,可以把所有关键点为根求出。
根据分块的一种方法(空间\(n\sqrt{n}\)),可以考虑预处理出\(f_{i,j}\)表示第\(i\)个关键点到根的节点有多少个\(j\)。
用\(f_{x',a_i}+f_{y',a_i}-f_{lca(x',y'),a_i}\)即可求出路径上的颜色为\(a_i\)的点数。
时空复杂度\(O(n\sqrt{n})\)
问题2:lg3603
链上显然可以分块,预处理每个块的bitset,查询时把对应块的bitset or起来后,对于边角暴力修改即可得到答案的bitset。
然后find_next即可。
树上显然可以树分块
随机分块后预处理关键点之间的bitset,查询把对应块的bitset or起来后,对于边角暴力修改即可得到答案的bitset。
问题3:IOI2009 regions
问题4:loj6115
问题5:hdu6271
简单题
随机分块后,考虑每个询问点跳到它的上面第一个关键点。
设询问\((x,y)\)为插入根到\((x,y)\)边的连通块个数
设一个询问跳到\((x',y')\)
则从\((x',y')\)向下插入\(\sqrt{n}\)条边就可以得到答案,然后撤销即可。
用并查集维护。
我们还需要维护插入根到\((x',y')\)边的连通性。
固定\(y'\),从根到\(x'\)dfs。
在dfs到某个点时维护这个点到父亲的边集即可。
王室联邦分块:
问题6:gty的妹子树
做法1:时间分块(定期重构)
做法很显然。
每\(\sqrt{n}\)个0/2操作后重构
如果没有修改,显然用可持久化线段树合并,查询子树中\(<a_x\)的值即可。
如果有修改,发现如果更新可持久化线段树需要全局更新。
如果不全局更新,考虑记录下所有被修改过的点,这些点显然不多。
对于0操作我们需要查询子树/父亲链对当前点的贡献,然后减去原来的贡献,加上现在的贡献,而2操作就不用
最多只需要扫描\(\sqrt{n}\)个点。
快速判定一个点是否是另一个点的祖先可以dfs序,然而重构后不能用dfs序。
考虑倍增,设要判定\(x\)是否是\(y\)的祖先,显然把\(y\)跳到和\(x\)同深度后,判定\(x\)是否等于\(y\)即可。
时间复杂度\(O(n\sqrt{n\log_2n})\)
问题7:「THUPC 2019」不用找的树
问题8:count
先考虑序列上怎么做。
\(O(n^2)\)做法:\(a_j-a_i=a_k-a_j\),可以枚举\(j,k\),\(i\)必须\(<j\)
这样子由\(j,k\)可以得到\(i\)的值:\(2a_j-a_k=a_i\)
维护桶即可。
优化显然考虑分块,注意到\(j,k\)在同一个块内时间复杂度较低。
分4种情况:
1.\(i,j,k\)在同一块:枚举\(j,k\),维护\(<j\)下标构成的桶
2.\(i\)在某一块,\(j,k\)在另一块:\(j,k\)最多枚举\(O(n\sqrt{n})\)次。
3.\(i,j\)在同一块,\(k\)在某一块:同情况2
4.\(i,j,k\)分别在不同的块内:此时\(2a_j=a_k+a_i\)
事实上,可以对于两边块内的所有元素的对应位置\(cnt_{a_i}++\),两边fft卷积即可。
考虑拓展到树上。
分块后
问题9:lg5064
问他10:rpdq