|
| 1 | +import java.util.*; |
| 2 | +import java.io.*; |
| 3 | + |
| 4 | +//LCA(최소 공통조상)+DFS |
| 5 | +//N(2 ≤ N ≤ 40,000) |
| 6 | +public class YJ_1761 { |
| 7 | + static class Node{ |
| 8 | + int num; |
| 9 | + int distance; |
| 10 | + Node(int num, int distance){ |
| 11 | + this.num = num; |
| 12 | + this.distance = distance; |
| 13 | + } |
| 14 | + } |
| 15 | + |
| 16 | + static int n; |
| 17 | + static int m; |
| 18 | + static int[][] dp; |
| 19 | + static int[] depth; |
| 20 | + static int[] distances; //노드 1~N 까지의 합 |
| 21 | + static boolean[] visited; |
| 22 | + static List<List<Node>> graph = new ArrayList<>(); |
| 23 | + |
| 24 | + static final int MAX = 16; //트리의 높이 log2^N 으로 최대 log2^40000 은 15.3 |
| 25 | + |
| 26 | + public static void main(String[] args) throws IOException{ |
| 27 | + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); |
| 28 | + StringTokenizer st = new StringTokenizer(br.readLine()); |
| 29 | + n = Integer.parseInt(st.nextToken()); |
| 30 | + |
| 31 | + //초기화 |
| 32 | + dp = new int[n+1][MAX+1]; |
| 33 | + depth = new int[n+1]; |
| 34 | + distances = new int[n+1]; |
| 35 | + visited = new boolean[n+1]; |
| 36 | + for(int i=0; i<n+1; i++){ |
| 37 | + graph.add(new ArrayList<>()); |
| 38 | + } |
| 39 | + //그래프 모델링 |
| 40 | + for(int i=0; i<n-1; i++){ |
| 41 | + st = new StringTokenizer(br.readLine()); |
| 42 | + int to = Integer.parseInt(st.nextToken()); |
| 43 | + int from = Integer.parseInt(st.nextToken()); |
| 44 | + int distance = Integer.parseInt(st.nextToken()); |
| 45 | + graph.get(to).add(new Node(from, distance)); |
| 46 | + graph.get(from).add(new Node(to, distance)); |
| 47 | + } |
| 48 | + dfs(1, 0); |
| 49 | + saveParents(); |
| 50 | + //정답 출력 |
| 51 | + m = Integer.parseInt(br.readLine()); |
| 52 | + while(m-- > 0){ |
| 53 | + st = new StringTokenizer(br.readLine()); |
| 54 | + int to = Integer.parseInt(st.nextToken()); |
| 55 | + int from = Integer.parseInt(st.nextToken()); |
| 56 | + int common = lca(to, from); //루트와 가장 가까운 공통 조상 찾기 |
| 57 | + System.out.println(distances[to] + distances[from] - (2*distances[common])); |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + static void dfs(int to, int from){ |
| 62 | + visited[to] = true; //★현재 시작점에 방문체크 |
| 63 | + depth[to] = depth[from] + 1; |
| 64 | + |
| 65 | + for(Node next : graph.get(to)){ |
| 66 | + if(!visited[next.num]){ //★현재와 이어진 다음 노드의 방문 유무 확인 |
| 67 | + visited[next.num] = true; |
| 68 | + dp[next.num][0] = to; //각 노드 별 직속 조상 저장으로 초기화 |
| 69 | + distances[next.num] = distances[to] + next.distance; //다음 노드에서 정점까지의 누적거리 |
| 70 | + dfs(next.num,to); |
| 71 | + } |
| 72 | + } |
| 73 | + } |
| 74 | + |
| 75 | + //전체 트리에서 각 노드별로 다중 차수 부모 노드들 저장 |
| 76 | + static void saveParents(){ |
| 77 | + for(int from=1; from<MAX; from++){ |
| 78 | + for(int to=1; to<n+1; to++){ |
| 79 | + int parent = dp[to][from-1]; |
| 80 | + dp[to][from] = dp[parent][from-1]; |
| 81 | + } |
| 82 | + } |
| 83 | + } |
| 84 | + |
| 85 | + static int lca(int to, int from){ |
| 86 | + if(depth[to] > depth[from]){ // from 이 항상 더 깊은 노드로 기준점 잡기 |
| 87 | + int temp = to; |
| 88 | + to = from; |
| 89 | + from = temp; //더 깊은 노드로 교체 |
| 90 | + } |
| 91 | + //각 노드 간 레벨이 다를 경우 깊이 맞추기 |
| 92 | + for(int i = MAX; i>=0; i--){ |
| 93 | + if(Math.pow(2,i) <= depth[from] - depth[to]){ //Math.pow 를 통해 깊이 레벨을 표현 (하나의 레벨에 있는 모든 가짓수) |
| 94 | + from = dp[from][i]; // 더 깊은 노드인 from 를 위로 올림 |
| 95 | + } |
| 96 | + } |
| 97 | + //깊이 레벨이 같고, 부모가 같다면 공통 조상 반환(to 를 반환해도 됨) |
| 98 | + if(to == from){ |
| 99 | + return from; |
| 100 | + } |
| 101 | + //깊이가 같은 상태지만, 부모가 다른 경우 루트와 가장 가까운 레벨의 공통 조상 찾기 |
| 102 | + for(int i=MAX; i>=0; i--){ |
| 103 | + if(dp[to][i] != dp[from][i]){ |
| 104 | + //to와 from의 부모가 다르면 상위 레벨로 계속 이동 |
| 105 | + to = dp[to][i]; |
| 106 | + from = dp[from][i]; |
| 107 | + } |
| 108 | + } |
| 109 | + return dp[from][0]; //to와 from의 부모가 같기 때문에 dp[to][0]를 반환해도 됨 |
| 110 | + } |
| 111 | + |
| 112 | +} |
0 commit comments