【初识】树上分块

树上的有些问题是能够用树剖或者动态树解决的,可是他们有一个动同点就是:不连通。

  • 好比求u到v的路径权值和,或者最大值:

                u到v可能对应了多个链,这多个链在对应的数据结构(假设是线段树)上面对应不一样的区间。可是线段树上这几个区间的不连续并不影响咱们获得答案。html

                (固然求子树的信息话是连续区间)ios

那么若是咱们遇到的问题要求区间连续呢,好比求u到v的路径上点的权值有多少种?若是不连续就得处理链与链之间的关系。显然这些点得待在一块儿,若是树剖很难维护链与链之间的关系。算法

树分块,大概有这样的一些方法: 

  • 王室联邦分块法:能够保证每一个块的大小和直径都不超过2√N1,可是不保证块联通 
  • DFS序分块法:首先是好写(毕竟转化成了序列问题),严格保证块大小√N,可是不保证直径,也不保证联通。处理子树信息比较方便 
  • size分块:检查当前节点的父亲所在块的大小,若是小于√N就把当前节点加入进去,否则新开块。块大小最坏√N,保证块内联通,还保证直径,多么优美啊惋惜不能保证块个数(一个菊花图就死了)

 

王室联邦分块,假设每一个块的数量个数为[B,3B].

     如何分组能够看,裸题 BZOJ1086 王室联邦。 数据结构

#include<cstdio> #include<iostream> #include<algorithm> #include<cstring>
using namespace std; int const maxn=2010; int q[maxn],group[maxn],rt[maxn],top; int Laxt[maxn],Next[maxn],To[maxn],cnt,ans; int n,B; void init() { ans=0;  top=0;  cnt=0; memset(Laxt,0,sizeof(Laxt)); } void add(int u,int v) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; } void dfs(int u,int pre) { int Now=top; for(int i=Laxt[u];i;i=Next[i]){ if(To[i]!=pre){ dfs(To[i],u); if(top-Now>=B) { rt[++ans]=u; while(top!=Now) group[q[top--]]=ans;} } } q[++top]=u; } int main() { while(~scanf("%d%d",&n,&B)){ int u,v; init(); for(int i=1;i<n;i++){ scanf("%d%d",&u,&v); add(u,v);add(v,u); } dfs(1,0); while(top) group[q[top--]]=ans;  printf("%d\n",ans); for(int i=1;i<=n;i++) printf("%d ",group[i]);printf("\n"); for(int i=1;i<=ans;i++) printf("%d ",rt[i]);printf("\n"); } return 0; }
View Code

 

再稍微介绍一下王室联邦分块是干吗的:

     咱们dfs,把子树中大于B的分为一组,剩余的(确定小于B)上传分到父亲那组。因为父亲那组大于B,加进去小于3B。每一组即比较平均了,B的大小会影响空间和时间的优劣,须要根据题目给定的时间和空间,时间多空间小就B大,空间多时间少就B小。从而来决定B的大小。ide

     这样分块是为了莫队的排序,而不是预处理保存信息。好比,(u,v) 转移到(a,b),因为u和a或在一个组里面,即距离不太远,转移时间不太大。spa

 

例题: Count on a tree II ,SPOJ - COT2,代码见这里

 求节点u到节点v路径上节点数值的种类。 此题有不少种牛逼作法,见此处的整理.net

对于不要求在线的题,咱们能够能够选择莫队算法:根据王室联邦分块来分块排序,按顺序转移获得答案,能够参考这里的代码code

相关文章
相关标签/搜索