[51nod 1766] 树上最远点对(线段树+树的直径)

本文介绍了一种高效算法,用于解决树上两点间最远距离的查询问题。通过利用线段树和LCA(最近公共祖先)的优化技巧,将复杂度降低到O(nlogn),适用于大规模数据集。

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

题意

  • 多次询问一个点在 [ a , b ] [a, b] [a,b], 另一个点在 [ c , d ] [c, d] [c,d]内的树上最远距离。

有一个结论对于两个联通块 S , T S, T S,T,设 d ( S ) d(S) d(S)表示联通块直径的两个端点,那么 d ( S ∪ T ) ∈ d ( S ) ∪ d ( T ) d(S∪T) ∈d(S)∪d(T) d(ST)d(S)d(T),这个东西仔细想下不是很难证,然后我们用线段树维护就好了。

但是每次 p u s h u p pushup pushup的时候都要求 l c a lca lca,复杂度是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)的,我们可以用 S T ST ST表求 l c a lca lca来优化,我们维护一下原树的欧拉序,也就是一个点进栈的时候把它加入序列,出栈的时候把它的父亲加入序列,那么两个点的 l c a lca lca就是两个点在序列中第一次出现位置中深度最小的点。

这样子求 l c a lca lca的复杂度就降到了 O ( n l o g n + q ) O(nlogn + q) O(nlogn+q)了,总复杂度就是 O ( n l o g n ) O(nlogn) O(nlogn)的。

#include<bits/stdc++.h>
#include<bits/extc++.h>

#define file(s) freopen(s".in", "r", stdin), freopen(s".out", "w", stdout)
#define go(x, i) for(register int i = head[x]; i; i = nxt[i])
#define For(i, a, b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++ i)
#define FOR(i, a, b) for(register int i = (a), i##_end_ = (b); i >= i##_end_; -- i)
#define debug(x) cout << #x << " = " << x << endl
#define mem(a, b) memset(a, b, sizeof(a))
#define cpy(a, b) memcpy(a, b, sizeof(a))
#define min(a, b) (a < b ? a : b)
#define max(a, b) (b < a ? a : b)
#define inf (0x3f3f3f3f)
#define INF (1e18)
#define pb push_back
#define mp make_pair
#define x first
#define y second

typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;
typedef std::pair<ll, int> PLI;
typedef std::pair<int, int> PII;
typedef long double ldb;
typedef double db;

namespace IO {
#define getc() ((S_ == T_) && (T_ = (S_ = Ch_) + fread(Ch_, 1, Buffsize, stdin), S_ == T_) ? 0 : *S_ ++)
#define putc(x) *nowps ++ = (x)
	const uint Buffsize = 1 << 15, Output = 1 << 23;
	static char Ch_[Buffsize], *S_ = Ch_, *T_ = Ch_;
	static char Out[Output], *nowps = Out;
	inline void flush(){fwrite(Out, 1, nowps - Out, stdout); nowps = Out;}
	template<class T>inline void read(T &_) {
		_ = 0; static char __; T ___ = 1;
		for(__ = getc(); !isdigit(__); __ = getc()) if(__ == '-') ___ = -1;
		for(; isdigit(__); __ = getc()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
		_ *= ___;
	}
	template<class T>inline void write(T _, char __ = '\n') {
		if(!_) putc('0');
		if(_ < 0) putc('-'), _ = -_;
		static uint sta[111], tp;
		for(tp = 0; _; _ /= 10) sta[++ tp] = _ % 10;
		for(; tp; putc(sta[tp --] ^ 48)); putc(__);
	}
	template<class T>inline bool chkmax(T &_, T __) {return _ < __ ? _ = __, 1 : 0;}
	template<class T>inline bool chkmin(T &_, T __) {return _ > __ ? _ = __, 1 : 0;}
}

using namespace std;
using namespace IO;

const int N = 1e5 + 10;
const int M = 2e5 + 10;

int head[N], to[M], v[M], nxt[M], e;
int dep[N], lg[M], pos[N], st[M][21];
int n, q, cnt;

void add(int x, int y, int z) {
	to[++ e] = y; nxt[e] = head[x]; head[x] = e; v[e] = z;
}

void dfs(int x, int fa) {
	st[pos[x] = ++ cnt][0] = x;
	go(x, i) if(to[i] ^ fa) {
		dep[to[i]] = dep[x] + v[i];
		dfs(to[i], x), st[++ cnt][0] = x;
	}
}

int _min(int x, int y) {
	return dep[x] < dep[y] ? x : y;
}

int lca(int x, int y) {
	x = pos[x], y = pos[y]; if(x > y) swap(x, y);
	int len = y - x + 1, lenn = lg[len];
	return _min(st[x][lenn], st[y - (1 << lenn) + 1][lenn]);
}

int get_dep(int x, int y) {
	return dep[x] + dep[y] - dep[lca(x, y)] * 2;
}

void Init() {
	int x, y, z, now = 0;
	read(n);
	For(i, 2, n) {
		read(x), read(y), read(z);
		add(x, y, z), add(y, x, z);
	}
	dfs(1, 0);
	For(i, 1, n << 1) {
		lg[i] = now; 
		if((1 << (now + 1)) == i) ++ now;
	}
	For(j, 1, 20) For(i, 1, cnt)
		if(i + (1 << (j - 1)) <= cnt)
			st[i][j] = _min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
}

struct Segment_Tree {
#define ls (bh << 1)
#define rs (ls | 1)
#define mid ((l + r) >> 1)
#define lson ls, l, mid
#define rson rs, mid + 1, r
	PII S[N << 2];

	PII merge(PII x, PII y) {
		PII res; int ans = 0;
		if(chkmax(ans, get_dep(x.x, x.y))) res = x;
		if(chkmax(ans, get_dep(y.x, y.y))) res = y;
		if(chkmax(ans, get_dep(x.x, y.y))) res = mp(x.x, y.y);
		if(chkmax(ans, get_dep(x.x, y.x))) res = mp(x.x, y.x);
		if(chkmax(ans, get_dep(x.y, y.x))) res = mp(x.y, y.x);
		if(chkmax(ans, get_dep(x.y, y.y))) res = mp(x.y, y.y);
		return res;
	}

	void build(int bh, int l, int r) {
		if(l == r) S[bh] = mp(l, r);
		else {
			build(lson), build(rson);
			S[bh] = merge(S[ls], S[rs]);
		}
	}

	PII query(int bh, int l, int r, int x, int y) {
		if(x <= l && r <= y) return S[bh];
		if(y <= mid) return query(lson, x, y);
		if(x > mid) return query(rson, x, y);
		return merge(query(lson, x, y), query(rson, x, y));
	}

}T;

int main() {
#ifdef ylsakioi
	file("1766");
#endif
	Init();
	T.build(1, 1, n);
	for(read(q); q -- ; ) {
		int l1, r1, l2, r2, ans = 0;
		read(l1), read(r1), read(l2), read(r2);
		PII L = T.query(1, 1, n, l1, r1), R = T.query(1, 1, n, l2, r2);
		chkmax(ans, get_dep(L.x, R.x)), chkmax(ans, get_dep(L.x, R.y));
		chkmax(ans, get_dep(L.y, R.x)), chkmax(ans, get_dep(L.y, R.y));
		write(ans);
	}
	return flush(), 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值