51Nod1766 树上的最远点对 ST表 LCA 线段树
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1766.html
题目传送门 - 51Nod1766
题意
n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c<=j<=d}
题解
只需要得到两个结论:
设 S(A) 为点集 A 的最远点对所包含的点的集合。
1. $S(A\cap B) \subset S(A)\cap S(B)$
2. 强制点对的两端点分别在集合 A,B 时,最终的点对就从 A,B 中各选一个,取距离最大的就好了。
于是只需要线段树维护一下区间最远点对即可。
时间复杂度 $O(n\log n)$ 。
代码
#include <bits/stdc++.h> using namespace std; int read(){ int x=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } const int N=100005,INF=1.05e9; struct Gragh{ static const int M=N*2; int cnt,y[M],z[M],nxt[M],fst[N]; void clear(){ cnt=1; memset(fst,0,sizeof fst); } void add(int a,int b,int c){ y[++cnt]=b,z[cnt]=c,nxt[cnt]=fst[a],fst[a]=cnt; } }g; #define list __zzd001 int n; int fa[N][20],depth[N],ld[N],Log[N*2]; int ST[N*2][20],list[N*2],dfn=0,in[N],out[N]; void dfs(int x,int pre,int d,int dd){ depth[x]=d,fa[x][0]=pre,ld[x]=dd; list[in[x]=++dfn]=x; for (int i=1;i<20;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for (int i=g.fst[x];i;i=g.nxt[i]) if (g.y[i]!=pre){ dfs(g.y[i],x,d+1,dd+g.z[i]); list[++dfn]=x; } out[x]=dfn; } int spmax(int a,int b){ return depth[a]<depth[b]?a:b; } void prework(){ for (int i=1;i<=dfn;i++){ ST[i][0]=list[i]; for (int j=1;j<20;j++){ ST[i][j]=ST[i][j-1]; if (i-(1<<(j-1))>0) ST[i][j]=spmax(ST[i][j-1],ST[i-(1<<(j-1))][j-1]); } } } int query(int L,int R){ int d=Log[R-L+1]; return spmax(ST[L+(1<<d)-1][d],ST[R][d]); } int LCA(int x,int y){ return query(min(in[x],in[y]),max(in[x],in[y])); } #define fi first #define se second #define pii pair <int,int> pii t[N<<2]; int dis(int x,int y){ if (!~x&&!~y) return -2*INF; if (!~x||!~y) return -INF; return ld[x]+ld[y]-2*ld[LCA(x,y)]; } pii Merge(pii a,pii b){ pii res=dis(a.fi,a.se)>dis(b.fi,b.se)?a:b; if (dis(a.fi,b.fi)>dis(res.fi,res.se))res=make_pair(a.fi,b.fi); if (dis(a.fi,b.se)>dis(res.fi,res.se))res=make_pair(a.fi,b.se); if (dis(a.se,b.fi)>dis(res.fi,res.se))res=make_pair(a.se,b.fi); if (dis(a.se,b.se)>dis(res.fi,res.se))res=make_pair(a.se,b.se); return res; } void build(int rt,int L,int R){ if (L==R){ t[rt]=make_pair(L,-1); return; } int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; build(ls,L,mid); build(rs,mid+1,R); t[rt]=Merge(t[ls],t[rs]); } pii query(int rt,int L,int R,int xL,int xR){ if (R<xL||L>xR) return make_pair(-1,-1); if (xL<=L&&R<=xR) return t[rt]; int mid=(L+R)>>1,ls=rt<<1,rs=ls|1; return Merge(query(ls,L,mid,xL,xR),query(rs,mid+1,R,xL,xR)); } int main(){ n=read(); g.clear(); for (int i=1;i<n;i++){ int a=read(),b=read(),c=read(); g.add(a,b,c); g.add(b,a,c); } dfs(1,0,0,0); Log[1]=0; for (int i=2;i<=dfn;i++) Log[i]=Log[i>>1]+1; prework(); build(1,1,n); int m=read(); while (m--){ int a=read(),b=read(),c=read(),d=read(); pii x=query(1,1,n,a,b); pii y=query(1,1,n,c,d); pii res=make_pair(-1,-1); if (dis(x.fi,y.fi)>dis(res.fi,res.se))res=make_pair(x.fi,y.fi); if (dis(x.fi,y.se)>dis(res.fi,res.se))res=make_pair(x.fi,y.se); if (dis(x.se,y.fi)>dis(res.fi,res.se))res=make_pair(x.se,y.fi); if (dis(x.se,y.se)>dis(res.fi,res.se))res=make_pair(x.se,y.se); printf("%d\n",dis(res.fi,res.se)); } return 0; }