UESTC 912 树上的距离 --LCA+RMQ+树状数组

时间:2021-08-09 10:16:40

1.易知,树上两点的距离dis[u][v] = D[u]+D[v]-2*D[lca(u,v)] (D为节点到根节点的距离)

2.某条边<u,v>权值一旦改变,将会影响所有以v为根的子树上的节点到根节点的距离,很明显,DFS一遍后以v为根的子树在DFS序列中是连续的一段,及转化为区间更新问题,可以用树状数组。

做法:先把求LCA解决,LCA可以转化为RMQ问题,可参见:LCA转RMQ, 即转化为LCA(T,u,v) = RMQ(B,pos[u],pos[v]),其中B为深度序列。预先DFS可以处理出深度序列,我这里用的是“另一种”深度序列,即时间戳表示的深度序列,用时间戳来代表深度,以及欧拉序列和pos数组,还有in[],out[]数组,表示每个节点DFS进出的时间戳。在DFS时间戳序列上建树状数组,值为每个节点与原来到根节点的距离相比的该变量。要用树状数组需要用一个巧妙的方法转为前缀和问题:在时间戳序列中,如果要更新v的子树,即为更新in[v],out[v],令a = in[v],b=out[v],则可以令A[a] = delta,A[b+1] = -delta。然后更新,就可以用树状数组来查询了。然后每次求两点间距离的时候,结果为固定的dis[u][v]+变动的值(树状数组query求出)。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 100007 struct Edge
{
int u,v,w,next;
}G[*N]; int first[N],tot,n,Time,ind,ola[*N],id;
int pos[N],in[*N],out[*N],vis[*N];
int d[*N][],dis[N],c[*N],T[*N],dp[*N]; void RMQ_init()
{
int i,j;
for(i=;i<=id;i++)
d[i][] = dp[i];
for(j=;(<<j)<=id;j++)
{
for(i=;i+(<<j)-<=id;i++)
d[i][j] = min(d[i][j-],d[i+(<<(j-))][j-]);
}
} int RMQ(int l,int r)
{
int k = ;
while((<<(k+)) <= r-l+)
k++;
return min(d[l][k],d[r-(<<k)+][k]);
} void addedge(int u,int v,int w)
{
G[tot].u = u;
G[tot].v = v;
G[tot].w = w;
G[tot].next = first[u];
first[u] = tot++;
} void init()
{
memset(G,,sizeof(G));
memset(first,-,sizeof(first));
memset(c,,sizeof(c));
memset(d,,sizeof(d));
tot = ;
Time = ;
ind = ;
id = ;
} void DFS(int u,int fa)
{
++Time;
int deep = Time;
ola[++ind] = u;
T[Time] = u;
dp[++id] = Time;
in[u] = Time;
for(int i=first[u];i!=-;i=G[i].next)
{
int v = G[i].v;
if(v == fa)
continue;
DFS(v,u);
dp[++id] = deep; //深度序列
ola[++ind] = u; //欧拉序列
}
//if(flag)
//ola[++ind] = u;
out[u] = Time;
} void DFSWG(int u,int w,int fa)
{
dis[u] = w;
for(int i=first[u];i!=-;i=G[i].next)
{
int v = G[i].v;
if(v == fa)
continue;
DFSWG(v,w+G[i].w,u);
}
} void Getpos()
{
memset(vis,,sizeof(vis));
for(int i=;i<=ind;i++)
{
if(!vis[ola[i]])
{
vis[ola[i]] = ;
pos[ola[i]] = i;
}
}
} int LCA(int u,int v)
{
int L = min(pos[u],pos[v]);
int R = max(pos[u],pos[v]);
return T[RMQ(L,R)];
} int lowbit(int x)
{
return x&(-x);
} void update(int k,int num)
{
while(k <= n)
{
c[k] += num;
k += lowbit(k);
}
} int query(int k)
{
int sum = ;
while(k > )
{
sum += c[k];
k -= lowbit(k);
}
return sum;
} int main()
{
int i,j,u,v,w,op,q;
scanf("%d",&n);
{
init();
for(i=;i<n-;i++)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
scanf("%d",&q);
DFS(,-);
Getpos();
RMQ_init();
DFSWG(,,-); //算出dis
while(q--)
{
scanf("%d%d%d",&op,&u,&v);
if(op == )
{
int lca = LCA(u,v);
printf("%d\n",dis[u]+dis[v]-*dis[lca]+query(in[u])+query(in[v])-*query(in[lca]));
}
else
{
int s = G[*u-].u;
int t = G[*u-].v;
if(pos[s] > pos[t]) //保证s是t的父亲
swap(s,t);
int delta = v - G[*u-].w;
G[*u-].w = G[*u].w = v; //更新权值
update(in[t],delta);
update(out[t]+,-delta);
}
}
}
return ;
}