题目大意:求树上任意两点距离。
思路:
dis[i]表示i到根的距离(手动选根),则u、v的距离=dis[u]+dis[v]-2*dis[lca(u,v)]。
lca:u~v的dfs序列区间里,深度最小的节点即为u、v的lca(最近公共祖先),RMQ把它找到。
难点:
何为dfs序:
就是树上dfs依次遍历的节点编号的序列。它的特点是回溯时会【再次】经过非叶节点,非叶节点在dfs序中出现多次,且dfs序列个数>节点个数。
为什么要用dfs序:
因为u、v的lca只出现在u、v的dfs序间。比如树1 3、1 2。dfs遍历的节点依次为1 3 1 2。3、2的lca为其中间的1。
RMQ的dp[i][j]的理解:
请把dp[][]看成一维数组,也就是只看第一维来理解这句话:i:dfs序为i,j:以i开始,长度为2^j的区间里depth最小的dfs序节点。
初始状态:依次储存dfs序(即dp[i][0]=i)。
状态转移:看看两个子区间的两个值,哪个depth更小,该区间就可以更新了。
实现:
lca实现:dfs序储存+depth[]+RMQ
dfs序实现:pos[](下标:节点编号,储存:dfs序)+ t[](下标:dfs序,储存:节点编号)(最后的dis[]查询要节点编号)
(pos[]既可以存节点的第一个dfs序也可以存最后一个dfs序,因为dfs序区间保证了包含lca,而那个depth最小的就是lca,dp会把它找到)
dep[]实现:下标dfs序,储存对应编号的depth
RMQ实现:dp[i][j]
dis[]实现:dfs会遍历每个节点一次或多次,dis[v]=0表示第一次,更新,dis[v]!=0表示v是回溯,跳过。
1 static IO io=new IO(); 2 static int n,m; 3 private static final int MAXN = 41000; 4 5 static class Edge{ 6 int v,next,dis; 7 8 public Edge(int v, int next, int dis) { 9 this.v = v; 10 this.next = next; 11 this.dis = dis; 12 } 13 } 14 15 // 边编号 16 static int size; 17 // 双向加边一定开2倍,数组越界异常在hdu里显示wa 18 static Edge[]edges=new Edge[MAXN<<1]; 19 static int[]head=new int[MAXN]; 20 static int[]dis=new int[MAXN]; 21 22 // dfs序,dfs完成后的意义是dfs序长度 23 static int tot; 24 // 这三个数组下标都是dfs序,不刷新,因为tot从0开始会依次覆盖 25 static int[]pos=new int[MAXN<<1]; 26 static int[]dep=new int[MAXN<<1]; 27 static int[]t=new int[MAXN<<1]; 28 29 // dp不刷新,因为状态转移只会处理子区间,2的次方保证可以对半分,手动赋初值 30 static int[][]dp=new int[MAXN<<1][22]; 31 32 public static void main(String[] args) { 33 int T=io.nextInt(); 34 while (T-->0){ 35 n=io.nextInt();m=io.nextInt(); 36 37 Arrays.fill(head,-1); 38 Arrays.fill(dis,0); 39 size=0; 40 for (int i = 0; i < n - 1; i++) { 41 int a=io.nextInt(),b=io.nextInt(),c=io.nextInt(); 42 edges[size]=new Edge(b,head[a],c); 43 head[a]=size++; 44 edges[size]=new Edge(a,head[b],c); 45 head[b]=size++; 46 } 47 48 tot=0; 49 dfs(1,0); 50 51 for (int i=0;i<tot;i++)dp[i][0]=i; 52 for (int j=1;(1<<j)<=tot;j++){ 53 for (int i=1;i+(1<<j)-1<=tot;i++){ 54 dp[i][j]= dep[dp[i][j-1]]<dep[dp[i+(1<<j-1)][j-1]]? 55 dp[i][j-1]:dp[i+(1<<j-1)][j-1]; 56 } 57 } 58 59 while (m-->0){ 60 int a=io.nextInt(),b=io.nextInt(); 61 // 因为dp[u][k]的u一定要左节点,dp[v-(1<<k)+1][k]的v一定要右节点 62 int u=Math.min(pos[a],pos[b]),v= Math.max(pos[a],pos[b]); 63 64 int k=(int)(Math.log(v-u+1)/Math.log(2)); 65 int dfsfa=dep[dp[u][k]]<dep[dp[v-(1<<k)+1][k]]? 66 dp[u][k]:dp[v-(1<<k)+1][k]; 67 int fa=t[dfsfa]; 68 io.println(dis[a]+dis[b]-2*dis[fa]); 69 } 70 } 71 } 72 73 static void dfs(int u,int de){ 74 pos[u]=tot;dep[tot]=de;t[tot++]=u; 75 for (int i=head[u];i!=-1;i=edges[i].next){ 76 if (dis[edges[i].v]!=0)continue; 77 dis[edges[i].v]=dis[u]+edges[i].dis; 78 dfs(edges[i].v,de+1); 79 // 这一步就保证了lca出现在u、v的dfs序间 80 // 如树1 3、1 2,dfs序为1 3 1 2,这一步保证了1 再次出现在3 2间 81 dep[tot]=de;t[tot++]=u; 82 } 83 }