树链剖分代码细解
总结回顾类文章,酌情观看。
Shape Of You
头图
Linux找图太难了呜呜
The club isn't the best place to find a lover
So the bar is where I go
Me and my friends at the table doing shots
Drinking faster and then we talk slow
You come over and start up a conversation with just me
And trust me I'll give it a chance now
Take my hand, stop, Put Van The Man on the jukebox
And then we start to dance, and now I'm singing like
Girl, you know I want your love
Your love was handmade for somebody like me
Come on now, follow my lead
I may be crazy, don't mind me, say
Boy, let's not talk too much
Grab on my waist and put that body on me
Come on now, follow my lead
Come—come on now, follow my lead
I'm in love with the shape of you
We push and pull like a magnet do
Although my heart is falling too
I'm in love with your body
And last night you were in my room
And now my bedsheets smell like you
Every day discovering something brand new
I'm in love with your body
HAOI2015 树上操作 代码拆分
void Wadd(int u,int v)
{
to[++cnt]=v;
ne[cnt]=hh[u];
hh[u]=cnt;
}
标准的链式前向星连边。
void Wpushup(int rt)
{
tsum[rt]=tsum[ls]+tsum[rs];
}
推上(更新)操作,进行一些更改后会随之变动的数据,如和与最大值。
void Wpushdown(int rt,int lenn)
{
tsum[ls]+=laz[rt]*(lenn-(lenn>>1));
tsum[rs]+=laz[rt]*(lenn>>1);
laz[ls]+=laz[rt],laz[rs]+=laz[rt];
laz[rt]=0;
}
推下(下放)操作,进行一些需要先处理的更改,主要为
void Wbuild(int rt,int l,int r)
{
if(l==r)
{
tsum[rt]=wt[l];
return;
}
Wbuild(ls,l,mid);
Wbuild(rs,mid+1,r);
Wpushup(rt);
}
建树。
void Wadp(int rt,int l,int r,int x,ll v)
{
if(l==r)
{
tsum[rt]+=v;
return;
}
if(laz[rt])
Wpushdown(rt,r-l+1);
if(x<=mid)
Wadp(ls,l,mid,x,v);
else
Wadp(rs,mid+1,r,x,v);
Wpushup(rt);
}
单点更改。(不更新会WA)
void Wadi(int rt,int l,int r,int x,int y,ll v)
{
if(x<=l&&r<=y)
{
laz[rt]+=v;
tsum[rt]+=v*(r-l+1);
return ;
}
if(laz[rt])
Wpushdown(rt,r-l+1);
if(x<=mid)
Wadi(ls,l,mid,x,y,v);
if(y>mid)
Wadi(rs,mid+1,r,x,y,v);
Wpushup(rt);
}
整条链修改(区间修改)。
ll Wqsum(int rt,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
return tsum[rt];
if(laz[rt])
Wpushdown(rt,r-l+1);
ll ans=0;
if(x<=mid)
ans+=Wqsum(ls,l,mid,x,y);
if(y>mid)
ans+=Wqsum(rs,mid+1,r,x,y);
return ans;
}
区间查询和。可以自己画个草图模拟一下,只要左右区间中有在所求范围内的就递归下去。
关于为什么这里pushdown后不用pushup
我个人的想法是:做修改操作时,已经将当前的
有不同想法也可以评论留言。
ll Wquesum(int x,int y)
{
ll ans=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])
swap(x,y);
ans+=Wqsum(1,1,n,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]<dep[y])
swap(x,y);
ans+=Wqsum(1,1,n,id[y],id[x]);
return ans;
}
求两个节点最短路径上权值和。利用已经求出的 LCA 相关信息不断向上取当前两点较深的那个至链首的区间和,然后跳到链首的父节点,重复上述操作,直到两点在同一条链上,一个区间和收尾。
void Wdfs1(int u,int f,int deep)
{
dep[u]=deep,fa[u]=f,siz[u]=1;
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
if(v==f)
continue;
Wdfs1(v,u,deep+1);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
}
dfs1 函数,负责求出点的深度,父节点,子树大小,以及重子节点编号,递归形式还是比较好理解的。
void Wdfs2(int u,int topf)
{
id[u]=++tot,top[u]=topf;
wt[tot]=w[u];
if(!son[u])
return;
Wdfs2(son[u],topf);
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
if(v==fa[u]||v==son[u])
continue;
Wdfs2(v,v);
}
}
dfs2 函数,负责求出每一个点的新的编号,新编号下的权值,以及所在链的链首。先遍历重子节点,在遍历轻子节点,轻子节点所在一条新的链,自己为链首。
short main()
{
memset(hh,-1,sizeof hh);
n=qr,m=qr;
fo(i,1,n)
w[i]=qr;
fo(i,1,n-1)
{
int a=qr,b=qr;
Wadd(a,b),Wadd(b,a);
}
Wdfs1(1,0,1),Wdfs2(1,1);
Wbuild(1,1,n);
while(m--)
{
int op=qr,x=qr;
if(op==1)
{
int y=qr;
Wadp(1,1,n,id[x],y);
}
else if(op==2)
{
int y=qr;
Wadi(1,1,n,id[x],id[x]+siz[x]-1,y);
}
else
printf("%lld\n",Wquesum(1,x));
}
return Ratio;
}
主函数。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】