大意: 给定n结点树, m个询问, 每次给出两个旅馆的位置, 求树上所有结点到最近旅馆距离的最大值
先考虑一些简单情形.
若旅馆只有一个的话, 显然到旅馆最远的点是直径端点之一
若树为链的话, 显然是直径端点或两旅馆的中点
这样的话思路就来了, 也就是说最远点要么是直径端点, 要么是到两旅馆距离差不超过1的点
实际求的时候并不需要求出具体在那个点取得最大, 只需简单分类讨论一下, 用RMQ预处理一下, O(1)回答询问
#include <iostream> #include <algorithm> #include <cstdio> #include <queue> #define REP(i,a,n) for(int i=a;i<=n;++i) #define pb push_back using namespace std; const int N = 1e5+10, INF = 0x3f3f3f3f; int n, m, r1, r2, mx; vector<int> g[N]; int vis[N], fa[N], s[N], dep[N], no[N]; int Log[N], w[N], f[2][20][N]; int bfs(int x) { memset(vis, 0, sizeof vis); queue<int> q; fa[x] = 0, vis[x] = 1, q.push(x); while (!q.empty()) { x = q.front();q.pop(); for (int y:g[x]) if (!vis[y]) { vis[y] = 1, fa[y]=x, q.push(y); } } return x; } void dfs(int x, int fa, int d, int rt) { dep[x]=d, no[x]=rt, w[rt] = max(w[rt], d); for (int y:g[x]) if (!vis[y]&&y!=fa) { dfs(y,x,d+1,rt); } } int RMQ(int l, int r, int p) { if (l>r) return -INF; int t = Log[r-l+1]; return max(f[p][t][l],f[p][t][r-(1<<t)+1]); } int main() { scanf("%d", &n); REP(i,2,n) { int u, v; scanf("%d%d", &u, &v); g[u].pb(v),g[v].pb(u); } r1 = bfs(1), r2 = bfs(r1); memset(vis, 0, sizeof vis); for (int i=r2; i; i=fa[i]) { vis[i]=1, s[++*s]=i, no[i]=*s; } REP(i,1,*s) dfs(s[i],0,0,i); Log[0] = -1; REP(i,1,n) { f[0][0][i]=w[i]+i,f[1][0][i]=w[i]-i; Log[i] = Log[i>>1]+1; } REP(j,1,19) for (int i=1;i+(1<<j-1)<=n; ++i) { f[0][j][i] = max(f[0][j-1][i],f[0][i+(1<<j-1)][j-1]); f[1][j][i] = max(f[1][j-1][i],f[1][i+(1<<j-1)][j-1]); } scanf("%d", &m); REP(i,1,m) { int x, y, ans; scanf("%d%d", &x, &y); int l=no[x], r=no[y]; if (l>r) swap(l,r),swap(x,y); if (l==r) { printf("%d\n", max(l-1,*s-l)+min(dep[x],dep[y])); continue; } int mid = l+r-dep[x]+dep[y]; if (mid<=2*l) ans = max(r-1,*s-r)+dep[y]; else if (mid>=2*r) ans = max(l-1,*s-l)+dep[x]; else { mid /= 2; int L=max(l-1,RMQ(l+1,mid,0)-l)+dep[x]; int R=max(*s-r,RMQ(mid+1,r-1,1)+r)+dep[y]; ans = max(L,R); } printf("%d\n", ans); } }