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