题意:有一棵树,每条边给定初始权值。一个人从s点出发。支持两种操作:修改一条边的权值;求从当前位置到点u的最短路径。
分析:就是在边可以修改的情况下求树上最短路。如果不带修改的话,用RMQ预处理LCA即可。
在静态版本的LCA问题上,用树状数组维护一条边在dfs序中表示的一段区间。为什么是一段区间,因为求该边之下的任意一点到根节点的距离都必须经过这条边。
用树状数组差分前缀和的方式维护区间修改。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define maxn 100005 struct Edge{ int to,next,id; }edge[maxn<<1]; int n,a[maxn],head[maxn],dep[maxn<<1],cnt,pos[maxn],dfs_seq[maxn<<1],dfn,f[maxn<<1][20]; int W[maxn],L[maxn],R[maxn],dfs_clock,C[maxn],G[maxn]; inline void add(int u,int v,int id) { edge[cnt].to=v; edge[cnt].next=head[u]; edge[cnt].id=id; head[u]=cnt++; } inline int lowbit(int x){return (x)&(-x);} void init() { memset(head,-1,sizeof(head)); memset(pos,-1,sizeof(pos)); memset(C,0,sizeof(C)); cnt=dfn=0; dfs_clock=0; } void dfs(int u,int deep) { dfs_seq[dfn]=u,dep[dfn]=deep,pos[u]=dfn++; L[u]=++dfs_clock; for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(pos[v]==-1){ G[edge[i].id]=v; dfs(v,deep+1); dfs_seq[dfn]=u,dep[dfn++]=deep; } } R[u]=dfs_clock; } void init_RMQ(int n) { for(int i=0;i<=n;++i) f[i][0]=i; for(int j=1;(1<<j)<=n;++j) for(int i=0;i+(1<<j)-1<=n;++i){ if(dep[f[i][j-1]]<dep[f[i+(1<<(j-1))][j-1]]) f[i][j]=f[i][j-1]; else f[i][j]=f[i+(1<<(j-1))][j-1]; } } inline int RMQ(int L,int R) { int k=0; while(1<<(k+1)<=R-L+1) ++k; if(dep[f[L][k]]<dep[f[R-(1<<k)+1][k]]) return f[L][k]; return f[R-(1<<k)+1][k]; } inline int lca(int u,int v) { if(pos[u]>pos[v]) return dfs_seq[RMQ(pos[v],pos[u])]; return dfs_seq[RMQ(pos[u],pos[v])]; } inline void update(int i,int x) { for(;i<=n;i+=lowbit(i)) C[i]+=x; } inline int sum(int i) { int s=0; for(;i>0;i-=lowbit(i)) s+=C[i]; return s; } int main() { int i,u,v,k,q,w,s; while(~scanf("%d%d%d",&n,&q,&s)){ init(); for(i=1;i<n;++i){ scanf("%d%d%d",&u,&v,&w); add(u,v,i); add(v,u,i); W[i]=w; } dfs(1,0); init_RMQ(dfn-1); u=s; for(i=1;i<n;++i){ update(L[G[i]],W[i]); update(R[G[i]]+1,-W[i]); } while(q--){ scanf("%d",&k); if(k){ scanf("%d%d",&u,&w); update(L[G[u]],w-W[u]); update(R[G[u]]+1,-w+W[u]); W[u]=w; } else{ scanf("%d",&v); printf("%d ",sum(L[s])+sum(L[v])-2*sum(L[lca(s,v)])); s=v; } } } return 0; }
树链剖分的方法也类似。剖出轻重链之后,可以快速求解lca,并用树状数组维护边的信息,同样是用差分的方式。
通常树链剖分维护的是树上点的信息,但本题可以用每条边在dfs树上的后继点来表示该边。
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int maxn =1e5+5; struct Edge{ int to,next; }E[2*maxn]; int n,head[maxn],tot; int idx,size[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn],l[maxn],r[maxn]; int edge[maxn][3]; int bit[maxn]; void init() { idx=tot=0; memset(head,-1,sizeof(head)); dep[1]=0,fa[1]=1,size[0]=0; memset(son,0,sizeof(son)); } void AddEdge(int u,int v) { E[tot] = (Edge){v,head[u]}; head[u]=tot++; } void dfs1(int u) { size[u]=1; for(int i=head[u];~i;i=E[i].next){ int v=E[i].to; if(v!=fa[u]){ fa[v]=u; dep[v]=dep[u]+1; dfs1(v); size[u]+=size[v]; if(size[son[u]]<size[v]) son[u]=v; } } } void dfs2(int u,int topu) { top[u]= topu; l[u] = ++idx; if(son[u]) dfs2(son[u],top[u]); for(int i=head[u];~i;i=E[i].next){ int v=E[i].to; if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } r[u] = idx; } void add(int pos,int val){ for(int i=pos;i<=n;i+= i&(-i)) bit[i]+=val; } inline int sum(int pos){ int res=0; for(int i=pos;i;i-= i&(-i)) res+=bit[i]; return res; } int lca(int u,int v){ while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]) swap(u,v); u = fa[top[u]]; } return dep[u]<dep[v] ? u:v; } int dist(int u,int v){ return sum(l[u])+sum(l[v]) - 2*sum(l[lca(u,v)]); } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int q,s,op,u,v,w; while(scanf("%d%d%d",&n,&q,&s)==3){ init(); memset(bit,0,sizeof(bit)); for(int i=1;i<n;++i){ scanf("%d%d%d",&u,&v,&w); AddEdge(u,v); AddEdge(v,u); edge[i][0] = u,edge[i][1] =v,edge[i][2] =w; } dfs1(1); dfs2(1,1); for(int i=1;i<n;++i){ if(dep[edge[i][0]]>dep[edge[i][1]]) swap(edge[i][0],edge[i][1]); v = edge[i][1]; add(l[v],edge[i][2]); add(r[v]+1,-edge[i][2]); } while(q--){ scanf("%d",&op); if(op==0){ scanf("%d",&u); printf("%d ",dist(s,u)); s = u; } else{ int k; scanf("%d%d",&k,&w); v = edge[k][1]; add(l[v],w-edge[k][2]); add(r[v]+1,-w+edge[k][2]); edge[k][2] =w; } } } return 0; }