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; }