首页 > 其他分享 >树链剖分代码细解

树链剖分代码细解

时间:2024-05-17 15:30:28浏览次数:25  
标签:rt qr 剖分 int void 树链 细解 laz my

总结回顾类文章,酌情观看。


Shape Of You
头图

Linux找图太难了呜呜
image

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;
    }

推下(下放)操作,进行一些需要先处理的更改,主要为 \(lazy\) 标记的下放。

    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);
    }

单点更改。\(l\) 与 \(r\) 为当前节点的左右边界,\(x\) 为修改的点编号,\(v\) 为更改后 / 增减的值。有 \(lazy\) 标记先下放,下放后随之更新。(不更新会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);
    }

整条链修改(区间修改)。\(x\),\(y\)分别为修改的左右边界,操作基本同上,加值时用 \(lazy\) 标记。

    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

我个人的想法是:做修改操作时,已经将当前的 \(sum\) 更新过了,即使 \(lazy\) 没有下放,导致的结果只会是当前 \(sum\) 仍是正确的,但子区间答案是未更新的,所以不会影响结果正确性。

有不同想法也可以评论留言。

    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;
	}

主函数。\(head\) 数组初始化,输入,连边。两次 dfs 初始化,建树,然后进行所需的操作。


完结撒花~

image

标签:rt,qr,剖分,int,void,树链,细解,laz,my
From: https://www.cnblogs.com/Ratio-Yinyue1007/p/18196799

相关文章

  • 树链剖分 学习笔记
    裂缝中的阳光-林俊杰头图有多少创伤卡在咽喉有多少眼泪滴湿枕头有多少你觉得不能够被懂的痛只能沉默有多少夜晚没有尽头有多少的寂寞无人诉说有多少次的梦还没做已成空等到黑夜翻面之后会是新的白昼等到海啸退去之后只是潮起潮落别到最后你才发觉心里头的......
  • 树链剖分 学习笔记
    树链剖分学习笔记时更。还没开始学,放个板子先。板子#include<bits/stdc++.h>#definefo(x,y,z)for(int(x)=(y);(x)<=(z);(x)++)#definefu(x,y,z)for(int(x)=(y);(x)>=(z);(x)--)typedeflonglongll;inlineintqr(){ charch=getchar();intx=0,f=1; for(;ch......
  • Java拦截器(Interceptor)详细解析
    转载链接地址:https://www.cnblogs.com/xiaoyh/p/16444681.html一、拦截器概念讲解拦截器的概念之前,我们先看一张图: (1)浏览器发送一个请求会先到Tomcat的web服务器(2)Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源(3)如果是静态资源,会直接到Tomcat......
  • 长链剖分
    我们现在有一棵有根树(节点的深度定义为根到它的距离)。设节点\(u\)的所有儿子中,深度最大的点为它的长儿子,记作\(son_u\)。(存在多个则任取一个,没有儿子则为空)记每个节点到它的长儿子的变为长边,其余边为短边。一段极长的全部由长边组成的链称为长链。特别的,按此过程划分后,不在......
  • 树链剖分
    树链剖分在DFS树上把连续的一段有祖先关系的单独开一个序列存储。查询每一个位置,不断地往链头条,然后跳到链头的父亲的链上\(\dots\)如果按DFS徐直接搞,会被以下数据hack可行的序列有\(:[110],[2,10],[3,12],[4,13],[5,14],[6,15],[7,16],[8,17],......
  • 树链剖分
    树链剖分我们一般使用重链剖分,就是子树大的先剖分,应为这样可以保证时间在\(log_n\)如图,先按照\(dfs\)序遍历出有一棵树,那么\(dfs\)序就是\([1,2,3,4,5,6,7,8,9]\),如果碰到一条边上\(dfn[f]-dfn[u]!=1\)则断一次,就可以剖出链了,如图,链为\([1,2,3][4,5]......
  • 树链剖分
    树链剖分,简称树剖,就是把一颗又大又高的树拆成一些链,方便使用某些数据结构。一般树剖我们随便DFS一下,将整棵树分成一些链,其中里面的DFS序连续。链的数量不管怎样是固定的\(O(N)\)。hack:某种DFS序是\((1,3,2,5,4,7,6,9,8,11,10)\),只要你不走运刚好,就仍然可以把单次询......
  • 在Windows防火墙设置中,允许单播响应(Unicast Response)是一个控制选项,用于允许或禁止系
    在Windows防火墙设置中,允许单播响应(UnicastResponse)是一个控制选项,用于允许或禁止系统对多播或广播网络流量的单播响应。让我详细解释一下允许和禁止单播响应的区别:允许单播响应(是):当设置为“是”时(默认值),Windows系统会允许对多播或广播网络流量的单播响应。这意味着当系......
  • 重链剖分题目选讲
    染色给定一棵\(n\)个节点的无根树,共有\(m\)个操作,操作分为两种:将节点\(a\)到节点\(b\)的路径上的所有点(包括\(a\)和\(b\))都染成颜色\(c\)。询问节点\(a\)到节点\(b\)的路径上的颜色段数量。颜色段的定义是极长的连续相同颜色被认为是一段。例如112221由......
  • Spring6 当中 Bean 的生命周期的详细解析:有五步,有七步,有十步
    1.Spring6当中Bean的生命周期的详细解析:有五步,有七步,有十步@目录1.Spring6当中Bean的生命周期的详细解析:有五步,有七步,有十步每博一文案1.1什么是Bean的生命周期1.2Bean的生命周期"五步"1.3Bean的生命周期“七步”1.4Bean的生命周期“十步”2.Bean的作用域不......