标签:+= 最短路 求和 line mat 维护 线段树 防止 roo
概念:
重儿子:父亲结点的所有儿子中子树结点数目最多(\(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;
}
标签:+= 最短路 求和 line mat 维护 线段树 防止 roo
原文地址:https://www.cnblogs.com/lhm-/p/12229481.html