POJ 2763 在线lca

本文介绍了一种解决树形结构中两点间距离查询及边长更新问题的方法。通过预处理获得每个节点到树根的距离,并利用最低公共祖先(LCA)算法快速计算任意两点间的路径长度。对于边长的变化,则采用递归更新子树内所有节点到根节点距离的技术。

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

链接:http://poj.org/problem?id=2763

题意:n个点构成树,q个询问,每次询问得到树上两点距离。

操作一:问从当前位置(起始在s)到v的距离(每次移动到新的地方)。

操作二:将第i条边的长度改为w


处理出到树根的距离,操作一每次用lca求dis[s] + dis[t] - 2*dis[lca(s,t)];操作二,更新某条边之后对这条边下面的子树到树根的距离更新。

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof((a)))
#define For(i,a,b) for(int (i)=(a);(i) < (b);(i)++)
#define Ror(i,a,b) for(int (i)=(a);(i) > (b);(i)--)
#define mp make_pair
#define pb push_back
#define inf 0x3f3f3f3f

void RI (int& x){
    x = 0;
    char c = getchar ();
    while (c == ' '||c == '\n')    c = getchar ();
    bool flag = 1;
    if (c == '-'){
        flag = 0;
        c = getchar ();
    }
    while (c >= '0' && c <= '9'){
        x = x * 10 + c - '0';
        c = getchar ();
    }
    if (!flag)    x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}

const int maxn = 200010;
const int maxm = 200010;

struct Side {
    int v,w,next;
}side[maxm];
int top,node[maxn];
void add_side(int u,int v,int w){
    side[top] = (Side){v,w,node[u]};
    node[u] = top++;
}

int ver[maxn];
int r_min[maxn][20];
int first[maxn],dis[maxn],R[maxn];//R深度
int tt;
void ST(int n){
    for(int i=1;i<=n;i++){
        r_min[i][0]=i;
    }
    int k=log((double)(n+1))/log(2.0);
    for(int j=1;j<=k;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            int a = r_min[i][j-1],b = r_min[i+(1<<(j-1))][j-1];
            r_min[i][j] = R[a] < R[b] ? a : b;
        }
    }
}
int RMQ(int l,int r){
    int k=log((double)(r-l+1))/log(2.0);
    int a = r_min[l][k],b = r_min[r-(1<<k)+1][k];
    return R[a] < R[b] ? a : b;
}
int lca(int a,int b){
    a = first[a];
    b = first[b];
    if(a > b)swap(a,b);
    int ans = RMQ(a,b);
    return ver[ans];
}
void dfs(int u,int dep){
    ver[++ tt] = u;first[u] = tt;R[tt] = dep;
    for(int i = node[u];i != -1;i = side[i].next){
        int v = side[i].v;
        if(first[v] == 0){
            dis[v] = dis[u] + side[i].w;
            dfs(v,dep+1);
            ver[++ tt] = u;R[tt] = dep;
        }
    }
}
struct BB{
    int s,t;
}bb[maxm];
void do_chg(int ss,int fa,int cc){
    dis[ss] += cc;
    for(int i = node[ss];i != -1;i = side[i].next){
        int v = side[i].v;
        if(v != fa)do_chg(v,ss,cc);
    }
}
void change(int fa,int son,int w){
    if(dis[fa] > dis[son])swap(fa,son);//cout<<fa<<' '<<son<<endl;
    int cc;
    for(int i = node[fa];i != -1;i = side[i].next){
        int v = side[i].v;
        if(v == son){
            cc = w - side[i].w;
            side[i].w = w;
            break;
        }
    }
    do_chg(son,fa,cc);
}
int main(){
    //freopen("test.txt","r",stdin);
    int n,q,s;
    while(~scanf("%d%d%d",&n,&q,&s)){
        mem(node,-1);top = 0;
        For(i,0,n-1){
            int a,b,c;
            RIII(a,b,c);
            add_side(a,b,c);
            add_side(b,a,c);
            bb[i+1] = (BB){a,b};
        }
        mem(first,0);
        tt = 0;dis[1] = 0;
        dfs(1,1);
        ST(2*n-1);
        while(q --){
            int op;
            RI(op);
            switch(op){
                case 0:{
                    int t;
                    RI(t);
                    cout<<dis[s] + dis[t] - 2*dis[lca(s,t)]<<endl;
                    //cout<<"fa:"<<lca(first[s],first[t])<<endl;
                    s = t;
                    break;
                }
                case 1:{
                    int i,w;
                    RII(i,w);
                    change(bb[i].s,bb[i].t,w);
                    break;
                }
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值