树链剖分模板+讲解

39 篇文章 6 订阅
30 篇文章 0 订阅

树链剖分的详解这里写得很好。我的标程与它的做法一样。
树链剖分的作用相当于在树上做线段树。
具体做法是通过一个点的dfs序来代替点的编号
设有重边和轻边
重边相连的子树是所有子树中大小最大的
那么它相连的点就叫做重儿子
为了保证一条重链上的dfs序连续,遍历时优先遍历重儿子
那么一条重链可以通过线段树直接处理
而一条路径最多经过log条链,可能是重链,可能是轻边组成的若干条长度为1的链
时间复杂度是log方的
找重链和重儿子通过两次递归实现
做法可以像下面标程一样,每次从x,y中找出其链顶深的进行处理
也可以把两边倒lca的分开做,打起来更方便一点,代码量差不多

模板题
【ZJOI2008】树的统计
在一颗树上,区间查询,单点修改。
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和

标程

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <iostream>
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define max(a,b) ((a)>(b)?(a):(b))
#define ll long long
#define N 30100
int n,a[N],last[N*10],next[N*10],to[N*10],tot,size[N],deep[N],dfn[N],son[N],fa[N],top[N],an;
struct note
{
    int mx,sum;
};
note tree[N*10];
void putin(int x,int y)
{
    next[++tot]=last[x];last[x]=tot;to[tot]=y;
}
void dg1(int x)
{
    size[x]=1;int jy=0;
    for(int i=last[x];i;i=next[i])
    {
        int k=to[i];
        if (deep[k]!=0) continue;
        fa[k]=x;deep[k]=deep[x]+1;dg1(k);size[x]+=size[k];
        if (size[k]>jy) son[x]=k,jy=size[k];
    }
}
void dg2(int x,int y)
{
    dfn[x]=++tot;if (son[x]) top[son[x]]=top[x],dg2(son[x],x);
    for(int i=last[x];i;i=next[i])
    {
        int k=to[i];
        if (k==son[x] || k==y) continue;
        top[k]=k;dg2(k,x);
    }
}
void change(int v,int i,int j,int x,int y)
{
    if (i==j) { tree[v].mx=y;tree[v].sum=y;return;}
    int mid;mid=(i+j)/2;
    if (x<=mid) change(v*2,i,mid,x,y); else change(v*2+1,mid+1,j,x,y);
    tree[v].mx=max(tree[v*2].mx,tree[v*2+1].mx);
    tree[v].sum=tree[v*2].sum+tree[v*2+1].sum;
}
void get(int v,int i,int j,int x,int y,int jy)
{
    if (i==x && j==y){ if (jy==1) an+=tree[v].sum;else an=max(an,tree[v].mx);return;}
    int mid;mid=(i+j)/2;
    if (y<=mid) get(v*2,i,mid,x,y,jy);
    else if(x>mid) get(v*2+1,mid+1,j,x,y,jy);
         else get(v*2,i,mid,x,mid,jy),get(v*2+1,mid+1,j,mid+1,y,jy);
}
int lct(int x,int y,int jy)
{
    int ans,bz=1;
    ans=jy==1?0:-2147483647;
    while (x!=y)
    {
        int f1=top[x],f2=top[y];an=jy==1?0:-2147483647;
        if (f1!=f2)
        {
            if (deep[f1]>=deep[f2])
            {
                get(1,1,tot,dfn[f1],dfn[x],jy);
                x=fa[f1];
            }
            else
            {
                get(1,1,tot,dfn[f2],dfn[y],jy);
                y=fa[f2];
            }
        }
        else
        {
            if (deep[x]<deep[y]) get(1,1,tot,dfn[x],dfn[y],jy); else get(1,1,tot,dfn[y],dfn[x],jy);
            if (jy==1) ans+=an;else ans=max(ans,an);
            bz=0;
            break;
        }
        if (jy==1) ans+=an;else ans=max(ans,an);
    }
    if (x!=0 && x==y && bz) 
    {
        an=jy==1?0:-2147483647;
        get(1,1,tot,dfn[x],dfn[y],jy);
        if (jy==1) ans+=an;else ans=max(ans,an);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    fo(i,1,n-1)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        putin(x,y);putin(y,x);
    }
    fo(i,1,n) scanf("%d",&a[i]);
    deep[1]=1;top[1]=1;dg1(1);
    tot=0;dg2(1,0);
    fo(i,1,n) change(1,1,tot,dfn[i],a[i]);
    int ac;scanf("%d\n",&ac);
    for(;ac;ac--)
    {
        char ch;scanf("%c",&ch);scanf("%c",&ch);
        int x,y;
        if (ch=='M')
        {
            scanf("AX %d %d\n",&x,&y);
            printf("%d\n",lct(x,y,2));
        }             
        if (ch=='S')
        {
            scanf("UM %d %d\n",&x,&y);
            printf("%d\n",lct(x,y,1));
        }
        if (ch=='H')
        {
            scanf("ANGE %d %d\n",&x,&y);
            change(1,1,tot,dfn[x],y);
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值