caioj 1431:Qtree2

本文深入解析了LCA算法的原理与应用,通过一道竞赛题详细展示了如何使用LCA算法解决复杂路径查询问题,包括距离查询和路径点查询。文章涵盖了算法的数据结构设计、预处理步骤以及查询操作的实现,适合对算法竞赛和图论算法感兴趣的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

【题目】
有N个点,编号为1到N,有N-1条边,每条边都有长度。
有若干个操作,操作分为两种
第一种:DIST u,v   表示询问u到v的距离。
第二种:KTH u,v,k  表示询问从u到v路径上第k个点的编号。
【输入格式】
输入一个整数T,表示有T组数据(1<=T<=25)
对于每组数据,第一行输入一个整数N,表示有N个点(1<=N<=10000)
下来N-1行每行输入三个整数x,y,c表示从编号为x的点到编号为y的点有一条长度为c的边。
下来若干个操作。读入“DONE”时停止。
操作详情见题目描述。
【输出格式】
对于每次操作输出相应答案即可。
【输入样例】

6
1 2 1
2 4 1
2 5 2
1 3 1
3 6 2
DIST 4 6
KTH 4 6 4
DONE
【输出样例】
5
3

 

因为不用在线修改,所以用LCA求解即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std ;

inline int read() {
	int x = 0 , f = 0 ; char s = getchar() ;
	while ( !isdigit(s) ) f |= s=='-' , s = getchar() ;
	while (  isdigit(s) ) x = (x<<1)+(x<<3)+(s-48) , s = getchar() ;
	return !f ? x : -x ;
}

const int N = 1e4 + 10 ;

struct edge {
	int v , w , nxt ;
} e[N<<1] ; int tot , last[N] ;
inline void add ( int u , int v , int w ) {
    e[++tot] = (edge){ v , w , last[u] } ;
    last[u] = tot ;
    e[++tot] = (edge){ u , w , last[v] } ;
    last[v] = tot ;
}

int fa[N][20] , dis[N] , dep[N] ;
void dfs ( int u , int f ) {
	fa[u][0] = f ; dep[u] = dep[f] + 1 ;
	for ( int i = 1 ; (1<<i) < dep[u] ; i ++ )
		fa[u][i] = fa[fa[u][i-1]][i-1] ;
	for ( int i = last[u] ; i != -1 ; i = e[i].nxt ) {
		int v = e[i].v ; if ( v == f ) continue ;
		dis[v] = dis[u] + e[i].w ; dfs ( v , u ) ;
	}
}
inline int LCA ( int x , int y ) {
	if ( dep[x] < dep[y] ) swap ( x , y ) ;
	for ( int i = 20 ; i >= 0 ; i -- )
		if ( dep[x]-dep[y] >= (1<<i) )
			x = fa[x][i] ;
	if ( x == y ) return x ;
	for ( int i = 20 ; i >= 0 ; i -- )
		if ( (1<<i) < dep[x] && fa[x][i] != fa[y][i] )
			x = fa[x][i] , y = fa[y][i] ;
	return fa[x][0] ;
}

int find ( int x , int up ) {
	if ( up == 0 ) return x ;
	for ( int i = 20 ; i >= 0 ; i -- )
		if ( (1<<i) <= up ) {
			up -= (1<<i) ; x = fa[x][i] ;
			if ( up == 0 ) break ;
		}
	return x ;
}

int main() {
	int T ; T = read() ;
	while ( T -- ) {
		int n = read() , u , v , w ;
		tot = -1 ; memset ( last , -1 , sizeof ( last )) ;
		for ( int i = 1 ; i < n ; i ++ ) {
			u = read() , v = read() , w = read() ;
			add ( u , v , w ) ;
		}
		dfs ( 1 , 0 ) ;
		char s[11] ;
		while ( ~scanf("%s", s ) ) {
			if ( s[1] == 'O' ) break ;
			u = read() , v = read() ;
			if ( s[1] == 'I' ) printf ( "%d\n" , dis[u]+dis[v]-(dis[LCA(u,v)]<<1) ) ;
			else {
				w = read() ; int t = LCA ( u , v ) ;
				if ( dep[u]-dep[t]+1 < w ) {
					w -= ( dep[u]-dep[t]+1 ) ;
					printf ( "%d\n" , find(v,dep[v]-dep[t]-w) ) ;
				}
				else printf ( "%d\n" , find(u,w-1) ) ;
			}
		}
	}
	return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值