重链剖分
概念:
重儿子:父亲结点的所有儿子中子树结点数目最多(\(size\)最大)的结点
轻儿子:父亲结点中除了重儿子以外的儿子
重边:父亲结点和重儿子连成的边
轻边:父亲结点和轻儿子连成的边
重链:由多条重边连接而成的路径
轻链:由多条轻边连接而成的路径
性质:
在轻边\((u,v)\)中,\(size(u)/2 \geqslant size(v)\)
从根结点到任意一点的路径上,不超过\(log\ n\)条轻链和\(log\ n\)条重链
时间复杂度为\(O(n\ log\ n)\)
先求出每个结点所在的子树大小,找到它的重儿子(即处理\(siz\)数组和\(son\)数组),记录其父亲以及深度(即处理\(fa\)数组和\(de\)数组)
\(code\):
void dfs_son(int x,int fath) { siz[x]=1; fa[x]=fath; de[x]=de[fath]+1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fath) continue; dfs_son(y,x); siz[x]+=siz[y]; if(siz[son[x]]<siz[y]) son[x]=y; } }
再将重儿子连接成重链,记录每个点所属重链的顶部端点(即处理\(top\)数组),记录每个结点的\(dfs\)序和\(dfs\)序所对应的结点(即处理\(dfn\)数组和\(rev\)数组)。为保证一条重链上各个结点\(dfs\)序连续,优先选择重儿子先\(dfs\)
\(code\):
void dfs_chain(int x,int tp) { dfn[x]=++dfn_cnt; rev[dfn_cnt]=x; top[x]=tp; if(son[x]) dfs_chain(son[x],tp);//dfs重儿子 for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(dfn[y]) continue; dfs_chain(y,y);//dfs轻儿子 } }
然后用数据结构(如线段树)来维护一条重链的信息
\(code\):
void pushup(int cur) { val[cur]=val[ls[cur]]+val[rs[cur]]; } void pushdown(int cur) { if(!lazy[cur]) return ; lazy[ls[cur]]+=lazy[cur]; lazy[rs[cur]]+=lazy[cur]; val[ls[cur]]+=lazy[cur]*(r[ls[cur]]-l[ls[cur]]+1); val[rs[cur]]+=lazy[cur]*(r[rs[cur]]-l[rs[cur]]+1); lazy[cur]=0; } void build(int L,int R,int &cur) { cur=++tree_cnt; l[cur]=L,r[cur]=R; if(L==R) { val[cur]=v[rev[L]];//将原结点的权值对应到dfs序上 return ; } int mid=(l[cur]+r[cur])>>1; build(L,mid,ls[cur]); build(mid+1,R,rs[cur]); pushup(cur); } ll query(int L,int R,int cur) { if(L<=l[cur]&&R>=r[cur]) return val[cur]; pushdown(cur); ll sum=0; int mid=(l[cur]+r[cur])>>1; if(L<=mid) sum+=query(L,R,ls[cur]); if(R>mid) sum+=query(L,R,rs[cur]); return sum; } void modify(int L,int R,ll add,int cur) { if(L<=l[cur]&&R>=r[cur]) { val[cur]+=add*(r[cur]-l[cur]+1); lazy[cur]+=add; return ; } pushdown(cur); int mid=(l[cur]+r[cur])>>1; if(L<=mid) modify(L,R,add,ls[cur]); if(R>mid) modify(L,R,add,rs[cur]); pushup(cur); } ll sum(int x,int y)//类似与LCA的过程 { ll ans=0; while(top[x]!=top[y]) { if(de[top[x]]<de[top[y]]) swap(x,y);//防止向上跳时跳过 ans+=query(dfn[top[x]],dfn[x],root);//处理这条重链的贡献 x=fa[top[x]]; } if(dfn[x]>dfn[y]) swap(x,y);//循环结束,x和y已经在一条重链上 return ans+query(dfn[x],dfn[y],root);//再加上两点间的贡献 } void update(int x,int y,ll add)//与求和同理 { while(top[x]!=top[y]) { if(de[top[x]]<de[top[y]]) swap(x,y); modify(dfn[top[x]],dfn[x],add,root); x=fa[top[x]]; } if(dfn[x]>dfn[y]) swap(x,y); modify(dfn[x],dfn[y],add,root); } ...... modify(dfn[x],dfn[x]+siz[x]-1,z,root);//将以x为根节点的子树内所有节点值都加上z query(dfn[x],dfn[x]+siz[x]-1,root)//求以x为根节点的子树内所有节点值之和 update(x,y,z);//将树从x到y结点最短路径上所有节点的值都加上z sum(x,y)//求树从x到y结点最短路径上所有节点的值之和
长链剖分
按深度剖分
\(maxde\),表示该节点为根的子树中的最大深度
可以用来求\(k\)次祖先和合并信息(HOT-Hotels,Dominant Indices)
\(code\):
void dfs_son(int x,int fa) { de[x]=maxde[x]=de[fa]+1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa) continue; dfs_son(y,x); maxde[x]=max(maxde[x],maxde[y]); if(maxde[son[x]]<maxde[y]) son[x]=y; } } int tmp[maxn],*f[maxn],*p=tmp; void memery(int x) { f[x]=p,p+=maxde[x]-de[x]+1; }
来源:https://www.cnblogs.com/lhm-/p/12229481.html