一棵树,支持:
- 路径加
- 单点查询
一般树上链的问题使用树链剖分解决。
重链剖分
前置知识
LCA,线段树
定义
重儿子:所有儿子中子树最大的儿子为重儿子
重边:重儿子之间的连边
重链:若干重儿子连成的链
性质
一棵树可以被剖成若干重链。
优先遍历重儿子,所有重链的dfs序连续。
重链数量不多于 \(\log_2\) 个。
一条链上经过的重链个数不多于 \(\log_2\) 个。
我们可以通过线段树维护一段重链的信息。
枚举链上的重链,查询可以做到 \(\log^2\)。
实现
预处理
int fat[100001],siz[100001],dep[100001],hson[100001],top[100001],cnt,dfn[100001],dis[100001];
void getdfsh(int u,int fa) //获取:父节点,深度,子树大小,重儿子
{
fat[u]=fa;
dep[u]=dep[fa]+1;
int lll=0;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==fa) continue;
getdfsh(v,u);
if(siz[v]>lll)
{
hson[u]=v;
lll=siz[v];
}
siz[u]+=siz[v];
}
siz[u]++;
}
void gettd(int u,int fa) //获取:链顶,dfs序
{
dfn[u] = ++cnt;
dis[u] = cnt;
if(hson[fat[u]]==u)
{
top[u]=top[fa];
}
else
{
top[u]=u;
}
if(hson[u]!=0) gettd(hson[u],u); //优先重子
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==fa||v==hson[u]) continue;
gettd(v,u);
}
}
修改与查询
int addlsum(int st,int ed,int v) //修改一条链,满足 lca(st,ed)=ed
{
if(dfn[st]<dfn[ed]) swap(st,ed); //交换
int u=st,re=0;
while(1)
{
if(dfn[top[u]]<=dfn[ed]) //当前重链顶超过终点了
{
add(1,1,n,dfn[ed],dfn[u],v);
break; //跳出
}
add(1,1,n,dfn[top[u]],dfn[u],v);
u=fat[top[u]];
}
return re;
}
int getlsum(int st,int ed) //查询一条链的和 同上
{
if(dfn[st]<dfn[ed]) swap(st,ed);get(1,1,n,dfn[ed],dfn[st]);
int u=st,re=0;
while(1)
{
if(dfn[top[u]]<=dfn[ed])
{
re+=get(1,1,n,dfn[ed],dfn[u]);
break;
}
re+=get(1,1,n,dfn[top[u]],dfn[u]);
u=fat[top[u]];
}
return re;
}
长链剖分
长链即按深度剖分,主要解决深度问题,如 \(k\) 级祖先。
标签:hson,剖分,int,siz,笔记,树链,100001,重链 From: https://www.cnblogs.com/lizhous/p/17372165.html