树链剖分

本文详细介绍了树链剖分的概念及应用,通过轻重儿子、轻重边等定义,将树形结构分解为轻重链,进而利用数据结构优化查询与更新操作。文章通过实例代码展示了如何使用线段树来提高查询效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树链剖分

什么是树链剖分

  不知道。
  重儿子Size最大的儿子。
  轻儿子:除了重儿子外。
  重边:与重儿子之间的边。
  轻边:与轻儿子之间的边。
  重链:连续的重边。
  轻链:同理。
  通过轻重链的剖分,这棵树非常成功地被我们弄得面目全非。
  这样我们再对两点间修改的时候修改的链是连续的,可证是log级别。

对于修改

  对于两点之间路径上的轻链直接进行修改(反正不会超过log条),而对于比较长的重链如果直接修改无疑是很慢的,所以想到可以用一些乱七八糟的数据结构来优化这个修改。
  我们平常的数据结构维护的都是链上的一些信息,所以考虑把树上的重链上的点弄成同一段连续区间,这个其实很容易通过dfs时优先遍历重儿子记下来即可。
  当然在实际修改的过程中需要跳过连续的一条链,这就需要再进行一次dfs来做出来某个点所在链的最高点是哪一个点(因为轻链长度仅为1,所以轻链的最高点是自己,而重链则要通过dfs来求出)。
  在dfs序上做一下线段树,查询的时候变成dfs序中的标号再查询。具体看代码

代码

#include <cstdio>
#include <cstring>
#include <iostream>

#define lc (o << 1)
#define rc ((o << 1) + 1)
#define mid ((l + r) >> 1)
#define INF 1000000007

using namespace std;

const int Maxn = 35000;

struct node {int to,next;}e[Maxn * 3];

int n,h[Maxn],w[Maxn],q,vis[Maxn],son[Maxn],maxx[Maxn * 4],cnt,tot;
int top[Maxn],num[Maxn],bel[Maxn],deep[Maxn],sum[Maxn * 4],fa[Maxn];

void Add(int u,int v) {e[++tot].to = v; e[tot].next = h[u]; h[u] = tot;}

void Dfs1(int u) {
    son[u] = 1;vis[u] = 1;
    for(int i = h[u];i;i = e[i].next) {
        int v = e[i].to;
        if(!vis[v]) {
            deep[v] = deep[u] + 1;
            fa[v] = u; Dfs1(v);
            son[u] += son[v];
        }
    }
}

void Dfs2(int u,int c) {
    int k = 0;
    top[u] = c;
    num[u] = ++cnt;
    bel[cnt] = w[u];
//  cout<<bel[cnt]<<endl;
    for(int i = h[u];i;i = e[i].next) {
        int v = e[i].to;
        if(deep[v] > deep[u] && son[v] > son[k]) k = v;
    }
    if(k == 0) return;
    Dfs2(k,c);
    for(int i = h[u];i;i = e[i].next) {
        int v = e[i].to;
        if(deep[v] > deep[u] && v != k) Dfs2(v,v);
    }
}

int Lca(int u,int v) {
    while(top[u] != top[v]) {
        deep[top[u]] < deep[top[v]] ? v = fa[top[v]] : u = fa[top[u]];
    }
    return deep[u] < deep[v] ? u : v;
}

void Update(int o) {
    sum[o] = sum[lc] + sum[rc];
    maxx[o] = max(maxx[lc] , maxx[rc]); 
}

void Build(int l,int r,int o) {
    if(l == r) {
        sum[o] = maxx[o] = bel[l];
//      cout<<bel[l]<<endl;
        return;
    }
    Build(l,mid,lc);
    Build(mid + 1,r,rc);
    Update(o);
}

void Change(int l,int r,int o,int x,int c) {
    if(l == r) {
        sum[o] = maxx[o] = c;
        return;
    }
    if(x <= mid) Change(l,mid,lc,x,c);
    if(x > mid) Change(mid + 1,r,rc,x,c);
    Update(o);
}

int QueryMax(int l,int r,int o,int a,int b) {
    if(a <= l && b >= r) return maxx[o];
    if(b <= mid) return QueryMax(l,mid,lc,a,b);
    else if(a > mid) return QueryMax(mid + 1,r,rc,a,b);
    else return max(QueryMax(l,mid,lc,a,mid) , QueryMax(mid + 1,r,rc,mid + 1,b));
}

int QuerySum(int l,int r,int o,int a,int b) {
    if(a <= l && b >= r) return sum[o];
    if(b <= mid) return QuerySum(l,mid,lc,a,b);
    else if(a > mid) return QuerySum(mid + 1,r,rc,a,b);
    else return QuerySum(l,mid,lc,a,b) + QuerySum(mid + 1,r,rc,mid + 1,b);  
}

int SolveMax(int u,int v) {
    int Max = -INF;
//  cout<<top[u]<<' '<<top[v]<<endl;
    while(top[u] != top[v]) {
        if(deep[top[v]] > deep[top[u]]) {
            Max = max(QueryMax(1,cnt,1,num[top[v]],num[v]) , Max);
            v = fa[top[v]];
        }
        else {
            Max = max(QueryMax(1,cnt,1,num[top[u]],num[u]) , Max);
            u = fa[top[u]];
        }
    }
    if(deep[u] > deep[v])swap(u,v);
    return max(Max , QueryMax(1,cnt,1,num[u],num[v]));
}

int SolveSum(int u,int v) {
    int Sum = 0;
    while(top[u] != top[v]) {
        if(deep[top[v]] > deep[top[u]]) {
            Sum += QuerySum(1,cnt,1,num[top[v]],num[v]);
            v = fa[top[v]];
        }
        else {
            Sum += QuerySum(1,cnt,1,num[top[u]],num[u]);
            u = fa[top[u]];
        }
    }
    if(deep[u] > deep[v])swap(u,v);
    return Sum + QuerySum(1,cnt,1,num[u],num[v]);
}

int main(){
//  freopen("bzoj_1036.in","r",stdin);
//  freopen("bzoj_1036.out","w",stdout);
    scanf("%d",&n);

    for(int i = 1;i < n;++i) {
        int x,y;
        scanf("%d%d",&x,&y);    
        Add(x,y);
        Add(y,x);
    }

    Dfs1(1);
    for(int i = 1;i <= n;++i)scanf("%d",&w[i]);
    Dfs2(1,1);
    Build(1,cnt,1);

//  for(int i = 1;i <= n;++i)printf("%d ",num[i]);
//  printf("\n");
//  for(int i = 1;i <= n;++i)printf("%d ",QuerySum(1,cnt,1,num[i],num[i]));

//  return 0;
    scanf("%d",&q);

    for(int i = 1;i <= q;i++) {
        char s[7];int x,y;
        scanf("%s",s);
        scanf("%d%d",&x,&y);
        if(s[0] == 'C') Change(1,n,1,num[x],y);
        else if(s[0] == 'Q' && s[1] == 'M') {
//          printf("!");
            printf("%d\n",SolveMax(x,y));
        }
        else {

            printf("%d\n",SolveSum(x,y));
        }
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值