poj 2763 lca(rmq)+树状数组(在一棵树上妈妈叫回家)

题意:给给定一棵含n个结点的树,树上的权值表示距离。共有q次操作,操作分为两种
0 c :求从当前位置s到c的距离,然后当前位置变成c

1 a b:把第a条边的权值变为b

北大校内2013年的一道题在这个题的基础上又增加了一个safe值的概念(题目链接http://poj.openjudge.cn/practice/1042/),safe值每次更新即可。

思路:树上的最短距离显然通过lca来求,而边权值会随时改变,所以求lca应该采用在线rmq方法。难点在如何更新,应用树状数组。可以深搜保存遍历该点的时间戳,s[i] 表示第一次遍历到该点的时间戳, t[i] 表示回溯到该点时的时间戳,这样每次修改边 i 的时候就可以对区间 [ s[i], t[i] ] 进行成段更新,用树状数组直接在这区间头加上c,区间尾后面减去c,然后求值时受影响的就只有这个区间了。(http://blog.csdn.net/tmeteorj/article/details/8520643)

#include <stdio.h>
#include <string.h>
#include <math.h>
#define swap(a,b,k) {k = a;a = b;b = k;}
#define min(a,b) ((a)<(b)?(a):(b))
#define clr(s,t) memset(s,t,sizeof(s))
#define N 100005
struct edge{
    int x,y,w,next;
}e[N<<1];
int first[N],flag[N<<1],s[N],t[N],d[N<<1],dis[N];
int tree[N<<1],dp[N<<1][20];
int n,q,top,now,len;
void init(){
    top = len = 0;
    clr(first,-1);
    clr(tree, 0);
    clr(dis, 0);
}
void add(int x,int y,int w){
    e[top].x = x;
    e[top].y = y;
    e[top].w = w;
    e[top].next = first[x];
    first[x] = top++;
}
void dfs(int x,int fa,int dep){
    int i,y;
    for(i = first[x];i!=-1;i=e[i].next){
        y = e[i].y;
        if(y != fa){
            dis[y] = dis[x]+e[i].w;
            flag[++len] = y;
            d[len] = dep+1;
            s[y] = len;
            dfs(y,x,dep+1);
            flag[++len] = x;
            d[len] = dep;
        }
    }
    t[x] = len;
}
void st(int n){
    int i,j,k = log((double)(n+1))/log(2.);
    for(i = 1;i<=n;i++)
        dp[i][0] = i;
    for(j = 1;j<=k;j++)
        for(i = 1;i+(1<<j)-1<=n;i++){
            if(d[dp[i][j-1]] < d[dp[i+(1<<(j-1))][j-1]])
                dp[i][j] = dp[i][j-1];
            else
                dp[i][j] = dp[i+(1<<(j-1))][j-1];
        }
}
int lowbit(int x){
    return x&(-x);
}
void addtree(int i,int x){
    int j;
    for(j = i;j<2*n;j+=lowbit(j))
        tree[j] += x;
}
int sum(int i){
    int j,res=0;
    for(j = i;j>=1;j-=lowbit(j))
        res += tree[j];
    return res;
}
int query(int a,int b){
    int k = log((double)(b-a+1))/log(2.);
    if(d[dp[a][k]] < d[dp[b-(1<<k)+1][k]])
        return dp[a][k];
    return dp[b-(1<<k)+1][k];
}
int main(){
    int i,j,k,a,b,w,order,lca,x,y;
    init();
    scanf("%d %d %d",&n,&q,&now);
    for(i = 1;i<n;i++){
        scanf("%d %d %d",&a,&b,&w);
        add(a,b,w);
        add(b,a,w);
    }
    flag[++len] = s[1] = 1;
    dfs(1,0,0);
    st(n*2-1);//初始化rmq
    for(i = 0;i<q;i++){
        scanf("%d",&order);
        if(order){
            scanf("%d %d",&a,&b);
            x = e[a*2-1].x;//取得边的两个端点
            y = e[a*2-1].y;
            j = b-e[a*2-1].w;
            e[a*2-1].w = e[a*2-2].w = b;//更新边的权值
            if(s[x] > s[y])//找到儿子,父亲一定在dfs中先被搜到,所以其s值一定较小
                swap(x, y, k);
            addtree(s[y],j);//用树状数组进行更新
            addtree(t[y]+1,-j);
        }else{
            scanf("%d",&b);
            a = now;
            now = b;
            if(s[a] > s[b])
                swap(a, b, k);
            lca = flag[query(s[a], s[b])];
            printf("%d\n",dis[a]+dis[b]-2*dis[lca] + sum(s[a])+sum(s[b])-2*sum(s[lca]));
        }
    }
}

北大竞赛题代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define N 100005
#define INF 0x7fffffff
struct edge{
    int y,next,w;
}e[N];
int T,n,fa[N],dr[N],safe[N],top,first[N],len;
int r[N],t[N],flag[N<<1],dp[N<<1][20],tree[N];
void init(){
    top = len = fa[1] = 0;
    clc(first,-1);
    dr[1] = 0;
    clc(tree,0);
}
void add(int x,int y,int w){
    e[top].y = y;
    e[top].w = w;
    e[top].next = first[x];
    first[x] = top++;
}
void createsafe(int x){
    safe[x] = 0;
    for(int i = first[x];i!=-1;i=e[i].next){
        createsafe(e[i].y);
        safe[x] = max(safe[x] , safe[e[i].y]+e[i].w);
    }
}
void dfs(int x,int dep){
    for(int i = first[x];i!=-1;i=e[i].next){
        int y = e[i].y;
        dr[y] = dr[x]+e[i].w;
        flag[++len] = y;
        r[y] = len;
        dfs(y,dep+1);
        flag[++len] = x;
    }
    t[x] = len;
}
void st(int n){
    int i,j,k = log((double)n)/log(2.);
    for(i = 1;i<=n;i++)
        dp[i][0] = i;
    for(j = 1;j<=k;j++)
        for(i = 1;i+(1<<j)-1 <= n;i++){
            if(flag[dp[i][j-1]] < flag[dp[i+(1<<(j-1))][j-1]])
                dp[i][j] = dp[i][j-1];
            else
                dp[i][j] = dp[i+(1<<(j-1))][j-1];
        }
}
int query(int a,int b){
    int k = log((double)(b-a+1))/log(2.);
    if(flag[dp[a][k]] < flag[dp[b-(1<<k)+1][k]])
        return flag[dp[a][k]];
    return flag[dp[b-(1<<k)+1][k]];
}
int update(int x){
    int tmp = 0;
    for(int i = first[x];i!=-1;i=e[i].next)
        tmp = max(tmp , safe[e[i].y]+e[i].w);
    if(safe[x] != tmp){
        safe[x] = tmp;
        return 1;
    }
    return 0;
}
int updatedr(int x,int y,int w){
    for(int i = first[x];i!=-1;i=e[i].next)
        if(e[i].y == y){
            int j = e[i].w;
            e[i].w = w;
            return j;
        }
    return 0;
}
int lowbit(int x){
    return x&(-x);
}
void jia(int j,int x){
    for(int i = j;i<2*n;i+=lowbit(i))
        tree[i] += x;
}
int sum(int x){
    int res = 0;
    for(int i = x;i>=1;i-=lowbit(i))
        res += tree[i];
    return res;
}
int main(){
    freopen("/Users/hetianjian/Desktop/ccc/a.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        int i,j,a,b,c,q,op,lca,now;
        scanf("%d",&n);
        init();
        for(i = 1;i<n;i++){
            scanf("%d %d %d",&a,&b,&c);
            if(a>b)
                swap(a,b);
            add(a,b,c);
            fa[b] = a;
        }
        createsafe(1);
        flag[++len] = r[1] = 1;
        dfs(1,0);
        st((n<<1)-1);
        scanf("%d",&q);
        now = 1;
        while(q--){
            scanf("%d\n",&op);
            if(op == 1){
                scanf("%d",&b);
                i = now;
                j = b;
                if(r[i]>r[j])
                    swap(i,j);
                lca = query(r[i],r[j]);
                printf("%d\n",dr[i]+dr[j]-2*dr[lca] + sum(r[i])+sum(r[j])-2*sum(r[lca]));
                now = b;
            }else if(op == 2){
                printf("%d\n",safe[now]);
            }else{
                scanf("%d %d %d",&a,&b,&c);
                if(a>b)
                    swap(a,b);
                j = updatedr(a,b,c);
                jia(r[b],c-j);
                jia(t[b]+1,j-c);
                
                while(a!=0){
                    if(!update(a))
                        break;
                    a = fa[a];
                }
            }
        }
    }
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值