Query on a tree II 倍增LCA

本文介绍了一种使用倍增算法求解树上两点间距离(LCA)及路径上第K个节点的问题,通过预处理提高查询效率,适用于竞赛编程。

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

You are given a tree (an undirected acyclic connected graph) with N nodes, and edges numbered 1, 2, 3...N-1. Each edge has an integer value assigned to it, representing its length.

We will ask you to perfrom some instructions of the following form:

  • DIST a b : ask for the distance between node a and node b
    or
  • KTH a b k : ask for the k-th node on the path from node a to node b

Example:
N = 6
1 2 1 // edge connects node 1 and node 2 has cost 1
2 4 1
2 5 2
1 3 1
3 6 2

Path from node 4 to node 6 is 4 -> 2 -> 1 -> 3 -> 6
DIST 4 6 : answer is 5 (1 + 1 + 1 + 2 = 5)
KTH 4 6 4 : answer is 3 (the 4-th node on the path from node 4 to node 6 is 3)

Input

The first line of input contains an integer t, the number of test cases (t <= 25). t test cases follow.

For each test case:

  • In the first line there is an integer N (N <= 10000)
  • In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of cost c (c <= 100000)
  • The next lines contain instructions "DIST a b" or "KTH a b k"
  • The end of each test case is signified by the string "DONE".

There is one blank line between successive tests.

Output

For each "DIST" or "KTH" operation, write one integer representing its result.

Print one blank line after each test.

Example

Input:
1

6
1 2 1
2 4 1
2 5 2
1 3 1
3 6 2
DIST 4 6
KTH 4 6 4
DONE

Output:
5
3

看到树上两点距离很容易想到LCA,
对于第K个点我们同样可以倍增解决;
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<bitset>
#include<ctime>
#include<time.h>
#include<deque>
#include<stack>
#include<functional>
#include<sstream>
//#include<cctype>
//#pragma GCC optimize(2)
using namespace std;
#define maxn 200005
#define inf 0x7fffffff
//#define INF 1e18
#define rdint(x) scanf("%d",&x)
#define rdllt(x) scanf("%lld",&x)
#define rdult(x) scanf("%lu",&x)
#define rdlf(x) scanf("%lf",&x)
#define rdstr(x) scanf("%s",x)
#define mclr(x,a) memset((x),a,sizeof(x))
typedef long long  ll;
typedef unsigned long long ull;
typedef unsigned int U;
#define ms(x) memset((x),0,sizeof(x))
const long long int mod = 9999973;
#define Mod 1000000000
#define sq(x) (x)*(x)
#define eps 1e-5
typedef pair<int, int> pii;
#define pi acos(-1.0)
//const int N = 1005;
#define REP(i,n) for(int i=0;i<(n);i++)
typedef pair<int, int> pii;

inline int rd() {
	int x = 0;
	char c = getchar();
	bool f = false;
	while (!isdigit(c)) {
		if (c == '-') f = true;
		c = getchar();
	}
	while (isdigit(c)) {
		x = (x << 1) + (x << 3) + (c ^ 48);
		c = getchar();
	}
	return f ? -x : x;
}


ll gcd(ll a, ll b) {
	return b == 0 ? a : gcd(b, a%b);
}
int sqr(int x) { return x * x; }



/*ll ans;
ll exgcd(ll a, ll b, ll &x, ll &y) {
	if (!b) {
		x = 1; y = 0; return a;
	}
	ans = exgcd(b, a%b, x, y);
	ll t = x; x = y; y = t - a / b * y;
	return ans;
}
*/

struct node {
	int u, v, w, nxt;
}e[maxn];
int head[maxn];
int tot;
int n;
int dis[maxn], dep[maxn];
int fa[maxn][20];
void init() {
	ms(e); ms(head); tot = 0; ms(dis); ms(dep);
	ms(fa);
}

void addedge(int u, int v, int w) {
	e[++tot].u = u; e[tot].v = v; e[tot].nxt = head[u]; e[tot].w = w;
	head[u] = tot;
}


void dfs(int rt) {
	for (int i = 1; i <= (int)log(n) / log(2) + 1; i++)
		fa[rt][i] = fa[fa[rt][i - 1]][i - 1];
	for (int i = head[rt]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (v == fa[rt][0])continue;
		fa[v][0] = rt; dep[v] = dep[rt] + 1;
		dis[v] = dis[rt] + e[i].w;
		dfs(v);
	}
}

int LCA(int x, int y) {
	if (dep[x] > dep[y])swap(x, y);
	for (int i = (int)log(n) / log(2) + 1; i >= 0; i--) {
		if (dep[fa[y][i]] >= dep[x])y = fa[y][i];
	}
	if (x == y)return x;
	for (int i = (int)log(n) / log(2) + 1; i >= 0; i--) {
		if (fa[x][i] != fa[y][i]) {
			x = fa[x][i]; y = fa[y][i];
		}
	}
	return fa[x][0];
}

int main()
{
//	ios::sync_with_stdio(0);
	int T = rd();
	while (T--) {
		n = rd();
		init();
		for (int i = 1; i < n; i++) {
			int u = rd(), v = rd(), w = rd();
			addedge(u, v, w); addedge(v, u, w);
		}
		dfs(1);
		char op[20];
		while (rdstr(op) != EOF && op[1] != 'O') {
			if (op[1] == 'I') {
				int u = rd(), v = rd();
			//	cout << dis[u] << ' ' << dis[v] << ' ' << dis[LCA(u, v)] << endl;
				printf("%d\n", dis[u] + dis[v] - 2 * dis[LCA(u, v)]);
			}
			else {
				int u = rd(), v = rd(), k = rd();
				int root = LCA(u, v);
				int ans;
				if (dep[u] - dep[root] + 1 >= k) {
					ans = dep[u] - k + 1;
					int i;
					for (i = 0; (1 << i) <= dep[u]; i++); i--;
					for (int j = i; j >= 0; j--) {
						if (dep[u] - (1 << j) >= ans)u = fa[u][j];
					}
					printf("%d\n", u);
				}
				else {
					ans = dep[root] + k - (dep[u] - dep[root] + 1);
					int i;
					for (i = 0; (1 << i) <= dep[v]; i++); i--;
					for (int j = i; j >= 0; j--) {
						if (dep[v] - (1 << j) >= ans)v = fa[v][j];
					}
					printf("%d\n", v);
				}
			}
		}
	}
	return 0;
}

 

转载于:https://www.cnblogs.com/zxyqzy/p/10355532.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值