树分块有两种方法:
1."王室联邦"分块:
一个点可以在多个块中。
每个块有一个顶部,每个块只能和其它块共用顶部。
一个块的顶部是它的所有点的lca,顶部深度最小。
每个块的深度次小的节点的父亲相同,都是块顶。
对于每个块,如果插入每个块的顶部,则块内每个节点必须连通。
删除所有块的顶部,最多只有1个块的大小不在范围([B,3B])之间。
分块方法:用栈和dfs,在dfs的过程中维护数组sz,表示当前和点(x)连通的,还未被分块的节点个数。
栈是未被分块节点集合。
当(szgeq B)时,把栈中所有节点删除,给这些节点新分配一个块。
事实上,这样子分块后每个块也能构成树结构:把每个块新建一个点,每个块的顶部也建一个节点。
每个块顶和每个块对应的节点连边。
如果(a)块和(b)块有公共点但是块顶不同,则连边。
块顶为(x)。
2.随机分块
在树上随机取(B)个关键点。
把关键点和根建立虚树。
虚树上的点就是关键点。
这个分块的性质是:一个点期望向父亲跳(frac{n}{B})次就能遇到关键点。
它在处理路径信息比较方便。
随机分块:
问题1:树上路径众数。
先把询问的((x,y))向跳到上面第一个关键点((x',y'))
如果(x'=y'),则可以暴力跳跃。
否则考虑((x',y'))路径上的众数,可以把所有关键点为根求出。
根据分块的一种方法(空间(nsqrt{n})),可以考虑预处理出(f_{i,j})表示第(i)个关键点到根的节点有多少个(j)。
用(f_{x',a_i}+f_{y',a_i}-f_{lca(x',y'),a_i})即可求出路径上的颜色为(a_i)的点数。
时空复杂度(O(nsqrt{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(nsqrt{nlog_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(nsqrt{n}))次。
3.(i,j)在同一块,(k)在某一块:同情况2
4.(i,j,k)分别在不同的块内:此时(2a_j=a_k+a_i)
事实上,可以对于两边块内的所有元素的对应位置(cnt_{a_i}++),两边fft卷积即可。
考虑拓展到树上。
分块后
问题9:lg5064
问他10:rpdq