题目大意:求树上任意两点距离。

思路:

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是回溯,跳过。

 static IO io=new IO();
static int n,m;
private static final int MAXN = 41000; static class Edge{
int v,next,dis; public Edge(int v, int next, int dis) {
this.v = v;
this.next = next;
this.dis = dis;
}
} // 边编号
static int size;
// 双向加边一定开2倍,数组越界异常在hdu里显示wa
static Edge[]edges=new Edge[MAXN<<1];
static int[]head=new int[MAXN];
static int[]dis=new int[MAXN]; // dfs序,dfs完成后的意义是dfs序长度
static int tot;
// 这三个数组下标都是dfs序,不刷新,因为tot从0开始会依次覆盖
static int[]pos=new int[MAXN<<1];
static int[]dep=new int[MAXN<<1];
static int[]t=new int[MAXN<<1]; // dp不刷新,因为状态转移只会处理子区间,2的次方保证可以对半分,手动赋初值
static int[][]dp=new int[MAXN<<1][22]; public static void main(String[] args) {
int T=io.nextInt();
while (T-->0){
n=io.nextInt();m=io.nextInt(); Arrays.fill(head,-1);
Arrays.fill(dis,0);
size=0;
for (int i = 0; i < n - 1; i++) {
int a=io.nextInt(),b=io.nextInt(),c=io.nextInt();
edges[size]=new Edge(b,head[a],c);
head[a]=size++;
edges[size]=new Edge(a,head[b],c);
head[b]=size++;
} tot=0;
dfs(1,0); for (int i=0;i<tot;i++)dp[i][0]=i;
for (int j=1;(1<<j)<=tot;j++){
for (int i=1;i+(1<<j)-1<=tot;i++){
dp[i][j]= dep[dp[i][j-1]]<dep[dp[i+(1<<j-1)][j-1]]?
dp[i][j-1]:dp[i+(1<<j-1)][j-1];
}
} while (m-->0){
int a=io.nextInt(),b=io.nextInt();
// 因为dp[u][k]的u一定要左节点,dp[v-(1<<k)+1][k]的v一定要右节点
int u=Math.min(pos[a],pos[b]),v= Math.max(pos[a],pos[b]); int k=(int)(Math.log(v-u+1)/Math.log(2));
int dfsfa=dep[dp[u][k]]<dep[dp[v-(1<<k)+1][k]]?
dp[u][k]:dp[v-(1<<k)+1][k];
int fa=t[dfsfa];
io.println(dis[a]+dis[b]-2*dis[fa]);
}
}
} static void dfs(int u,int de){
pos[u]=tot;dep[tot]=de;t[tot++]=u;
for (int i=head[u];i!=-1;i=edges[i].next){
if (dis[edges[i].v]!=0)continue;
dis[edges[i].v]=dis[u]+edges[i].dis;
dfs(edges[i].v,de+1);
// 这一步就保证了lca出现在u、v的dfs序间
// 如树1 3、1 2,dfs序为1 3 1 2,这一步保证了1 再次出现在3 2间
dep[tot]=de;t[tot++]=u;
}
}

How far away ? HDU - 2586 【LCA】【RMQ】【java】的更多相关文章

  1. hdu 2586(裸LCA)

    传送门 题意: 某村庄有n个小屋,n-1条道路连接着n个小屋(无环),求村庄A到村庄B的距离,要求是经过任一村庄不超过一次. 题解: 求出 lca = LCA(u,v) , 然后答案便是dist[u] ...

  2. How far away ? HDU - 2586

    How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  3. HDU 2586(LCA欧拉序和st表)

    什么是欧拉序,可以去这个大佬的博客(https://www.cnblogs.com/stxy-ferryman/p/7741970.html)巨详细 因为欧拉序中的两点之间,就是两点遍历的过程,所以只 ...

  4. 【10.10校内测试】【线段树维护第k小+删除】【lca+主席树维护前驱后驱】

    贪心思想.将a排序后,对于每一个a,找到对应的删除m个后最小的b,每次更新答案即可. 如何删除才是合法并且最优的?首先,对于排了序的a,第$i$个那么之前就应该删除前$i-1$个a对应的b.剩下$m- ...

  5. 【LCA求最近公共祖先+vector构图】Distance Queries

    Distance Queries 时间限制: 1 Sec  内存限制: 128 MB 题目描述 约翰的奶牛们拒绝跑他的马拉松,因为她们悠闲的生活不能承受他选择的长长的赛道.因此他决心找一条更合理的赛道 ...

  6. HDU - 2586 How far away ?(LCA模板题)

    HDU - 2586 How far away ? Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & ...

  7. hdu 2586 How far away ?倍增LCA

    hdu 2586 How far away ?倍增LCA 题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2586 思路: 针对询问次数多的时候,采取倍增 ...

  8. LCA(最近公共祖先)--tarjan离线算法 hdu 2586

    HDU 2586 How far away ? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/ ...

  9. 【Java】代处理?代理模式 - 静态代理,动态代理

    >不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算, ...

随机推荐

  1. Openssl x509命令

    一.简介 x509指令是一个功能很丰富的证书处理工具.可以用来显示证书的内容,转换其格式,给CSR签名等 二.语法 openssl x509 [-inform DER|PEM|NET] [-outfo ...

  2. dede 5.7 任意用户重置密码前台

    返回了重置的链接,还要把&amp删除了,就可以重置密码了 结果只能改test的密码,进去过后,这个居然是admin的密码,有点头大,感觉这样就没有意思了 我是直接上传的一句话,用菜刀连才有乐趣 ...

  3. Stream02

    import 'package:flutter/material.dart';import 'dart:async';import 'dart:math'; void main()=>runAp ...

  4. java 面试题整理(不定期更新)

    一.Java基础 1.Java面向对象的三个特征与含义 三大特征是:封装.继承和多态. 封装是指将某事物的属性和行为包装到对象中,这个对象只对外公布需要公开的属性和行为,而这个公布也是可以有选择性的公 ...

  5. Linux启动时间优化-内核和用户空间启动优化实践

    关键词:initcall.bootgraph.py.bootchartd.pybootchart等. 启动时间的优化,分为两大部分,分别是内核部分和用户空间两大部分. 从内核timestamp 0.0 ...

  6. Java的get、post请求

    URLConnection package com.shuzf.http; import java.io.BufferedReader; import java.io.IOException; imp ...

  7. 从 0 到 1 实现 React 系列 —— 1.JSX 和 Virtual DOM

    看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...

  8. linux nohup命令实现退出终端后程序继续后台运行

    Unix/Linux下一般想让某个程序在后台运行,很多都是使用&在程序结尾来让程序自动运行:但如果要想在退出终端后,程序依然还在后台运行,则要用nohup与&组合来实现. nohup ...

  9. DP求树的重心

    #include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> ...

  10. Python之使用转义序列 \n 和 \t 跟 expandtabs 函数输出表格

    示例: text = "username\temail\tpassword\nashdfh\tfiodfh@q.com\ty567\nsdfiuh\tadfhisoj@163.com\t42 ...