边权更新 查询路径长度和最大边权 【树链剖分 模板】

本文介绍了一种高效的数据结构——树链剖分技术,并结合线段树实现快速查询与更新操作。适用于解决涉及树状结构的复杂问题,如路径长度查询和边权最大值查找。

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

题意 给n个点的树,有两种操作 op x y ,第一种操作,将第x条边修改权值为y,第二种操作查询x到y的路径长度,第三种操作就是查询x到y的路径中边权的最大值。

今天刚学,说下我目前的理解,树链剖分就是将树上的边按照一种序排列到线段树上,从而进行快速的查询和更新。
对树的拆分,分为轻链和重链,快速的查询多是在重链上,轻链一般还是要一个个走,所以查询的时候,多是将轻链上的点跳到重链上,从而对多个重链来处理达到查询的目的。

【ps 练习打的,代码不一定对】
代码

#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
#define ll o<<1
#define rr o<<1|1
#define lson o<<1,le,mid
#define rson o<<1|1,mid+1,ri

const int MAXN = 1e3 ;
const int MAXM = 1e5 ;
const int mod  = 1e9+7 ;
const int inf  = 0x3f3f3f3f ;  

struct Tree{
    int l,r;
    LL sum,Max;
}tree[MAXN<<2];
void Up(int o) {
    tree[o].sum=tree[ll].sum+tree[rr].sum;
    tree[o].Max=max(tree[ll].Max,tree[rr].Max);
}
void Build(int o,int le,int ri){
    tree[o].l=le;tree[o].r=ri;
    tree[o].sum=0;tree[o].Max=0;
    if(le==ri) return ;
    int mid=(le+ri)>>1;
    Build(lson);Build(rson);
    Up(o);
}
void UpDate(int o,int pos,int val){  // 边权更新  
    if(tree[o].l==tree[o].r) {
        tree[o].sum=val;
        tree[o].Max=val;
        return ;
    }
    int mid=(tree[o].l+tree[o].r)>>1;
    if(pos>mid) UpDate(rr,pos,val);
    else if(pos<=mid) UpDate(ll,pos,val);
    Up(o);
}
LL QuerySum(int o,int le,int ri){
    if(tree[o].l>=le&&tree[o].r<=ri) return tree[o].sum;
    int mid=(tree[o].l+tree[o].r)>>1;
    if(le>mid) return QuerySum(rr,le,ri);
    else if(ri<=mid) return QuerySum(ll,le,ri);
    else return QuerySum(ll,le,mid)+QuerySum(rr,mid+1,ri);
}
LL QueryMax(int o,int le,int ri){
    if(tree[o].l>=le&&tree[o].r<=ri) return tree[o].Max;
    int mid=(tree[o].l+tree[o].r)>>1;
    if(le>mid) return QueryMax(rr,le,ri);
    else if(ri<=mid) return QueryMax(ll,le,ri);
    else return max(QueryMax(ll,le,mid),QueryMax(rr,mid+1,ri)); 
}

   // 以上为线段树模板部分  
struct Edge {
    int from,to,next;
}edge[MAXN<<1];
int head[MAXN],topp;
void init(){
    memset(head,-1,sizeof(head));
    topp=0;
}
void addedge(int a,int b){
    Edge e={a,b,head[a]};
    edge[topp]=e;head[a]=topp++;
}
int son[MAXN],num[MAXN];
int top[MAXN],pos[MAXN],id;
int dep[MAXN],pre[MAXN];
void Dfs1(int u,int fa,int d){
    dep[u]=d;pre[u]=fa;son[u]=-1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        Edge e=edge[i];
        if(e.to==fa) continue;// 自环跳过 
        Dfs1(e.to,u,d+1);
        num[u]+=num[e.to];
        if(son[u]==-1||num[son[u]]<num[e.to]) son[u]=e.to;
    }
}
void Dfs2(int u,int t){
    top[u]=t; pos[u]=++id;
    if(son[u]==-1) return ;// 子叶节点
    Dfs2(son[u],t); // 走重链
    for(int i=head[u];i!=-1;i=edge[i].next){
        Edge e=edge[i];
        if(e.to==pre[u]||e.to==son[u]) continue;//如果自环或者是重链 跳过。 
        Dfs2(e.to,e.to); // 走轻链,这里可以知道,不是重链上的点,top[v]=v; 
    }   
}

LL GetSum(int u,int v){  /* 路径求和  */
    int f1=top[u],f2=top[v];
    LL ans=0;
    while(f1!=f2) {// 不在一条链上。  
        if(dep[f1]<dep[f2]) {
            swap(u,v);  swap(f1,f2);
        } 
        ans+=QuerySum(1,pos[f1],pos[u]); // 每次让深的节点上跳。 轻链就跳一个节点,重链就跳到头节点。 
        u=pre[f1],f1=top[u];
    }
    if(u==v) return ans;
    if(dep[u]>dep[v]) swap(u,v); 
    return ans+=QuerySum(1,pos[son[u]],pos[v]);
}

LL GetMax(int u,int v){  /*  路径上边权最大     */
    int f1=top[u];int f2=top[v];
    LL ans=0;
    while(f1!=f2){
        if(dep[f1]<dep[f2]) {
            swap(f1,f2);swap(u,v);
        }
        ans=max(ans,QueryMax(1,pos[f1],pos[u]));
        u=pre[f1];f1=top[u];
    } 
    if(u==v) return ans;
    if(dep[u]>dep[v]) swap(u,v);
    return max(ans,QueryMax(1,pos[son[u]],pos[v]));
}
int s[MAXN],e[MAXN],c[MAXN];
int main(){
    int n,q;
    while(scanf("%d%d",&n,&q)!=EOF){
        init();
        for(int i=1;i<=n-1;i++) {
            scanf("%d %d %d",&s[i],&e[i],&c[i]);
            addedge(s[i],e[i]);
            addedge(e[i],s[i]); 
        }
        Dfs1(1,-1,1);     
        id=0; Dfs2(1,1);  
        Build(1,1,id);   
        for(int i=1;i<=n-1;i++){// 一个节点和其父边相对应,序号为线段树中的位置。pos[v]    
            if(dep[s[i]]>dep[e[i]]) swap(s[i],e[i]);  // 所以这里要找到最深的节点。 
            UpDate(1,pos[e[i]],c[i]);  //线段树赋初值。 
        }
        while(q--){
            int op,x,y;
            scanf("%d%d%d",&op,&x,&y);
            if(op==0) UpDate(1,pos[e[x]],y);
            else if(op==1)  printf("%lld\n",GetSum(x,y));
            else if(op==2)  printf("%lld\n",GetMax(x,y));
        }
    }
    return  0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值