[51nod1766][线段树][树的直径]树上的最远点对

51nod1766

可以发现,如果把两个区间分别求一个直径,记录一下端点,那么最优解一定是这四个端点中的任意两个构成的,且这两个点不能来自同一个区间,用直径的性质很好证明
然后我们就可以用一个线段树维护一下下标区间的直径的端点
然后,本题卡常,要用rmq-lca才能过

Code:

#include<bits/stdc++.h>
#define ls tr[k].l
#define rs tr[k].r
#define fi first
#define se second
#define mid ((ls+rs)>>1)
#define pi pair<int,int>
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=2e5+5;
namespace LCA{
	int vis[N<<1],nxt[N<<1],c[N<<1],head[N<<1],tot=0;
	inline void add(int x,int y,int z){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;c[tot]=z;}
	int d[N<<1],pt[N],dep[N],sta[N<<1],cnt=0,fipos[N];
	void dfs(int v,int depp){
		pt[v]=1;sta[++cnt]=v;fipos[v]=cnt;d[cnt]=depp;
		for(int i=head[v];i;i=nxt[i]){
			int y=vis[i];
			if(pt[y]) continue;
			dep[y]=dep[v]+c[i];
			dfs(y,depp+1);
			sta[++cnt]=v;d[cnt]=depp;
		}
	}
	int f[N<<1][21];
	void init(){
		for(int i=1;i<=cnt;i++) f[i][0]=i;
		int t=log(cnt)/log(2)+1;
		for(int j=1;j<t;j++)
			for(int i=1;i<=cnt-(1<<j)+1;i++){
				int x=f[i][j-1],y=f[i+(1<<(j-1))][j-1];
				f[i][j]=(d[x]<d[y])?x:y;
			}
	}
	inline int query(int l,int r){
		int t=log(r-l+1)/log(2);
		int x=f[l][t],y=f[r-(1<<t)+1][t];
		return (d[x]<d[y])?x:y;
	}
	inline int lca(int x,int y){int l=fipos[x],r=fipos[y];if(l>r) swap(l,r);return sta[query(l,r)];}
	inline int ask(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
}
using namespace LCA;
namespace segtree{
	struct seg{int l,r,u,v;}tr[N<<1];
	inline void pushup(int k){
		int u[5];u[1]=tr[k<<1].u,u[2]=tr[k<<1|1].u;
		u[3]=tr[k<<1].v,u[4]=tr[k<<1|1].v;
		int maxn=0;
		for(int i=1;i<=4;i++)
			for(int j=i+1;j<=4;j++){
				if(u[i]==-1 || u[j]==-1) continue;
				int dis=dep[u[i]]+dep[u[j]]-2*dep[lca(u[i],u[j])];
				if(dis>maxn) maxn=dis,tr[k].u=u[i],tr[k].v=u[j];
			}
	}
	inline pi merge(pi a,pi b){
		pi ret;
		int u[5];u[1]=a.fi,u[2]=b.fi;u[3]=a.se,u[4]=b.se;
		int maxn=0;
		for(int i=1;i<=4;i++)
			for(int j=i+1;j<=4;j++){
				if(u[i]==-1 || u[j]==-1) continue;
				int dis=dep[u[i]]+dep[u[j]]-2*dep[lca(u[i],u[j])];
				if(dis>maxn) maxn=dis,ret=make_pair(u[i],u[j]);
			}
		return ret;
	}
	void build(int k,int l,int r){
		ls=l,rs=r;
		if(l==r) {tr[k].u=l;tr[k].v=l;return;}
		build(k<<1,l,mid);build(k<<1|1,mid+1,r);
		pushup(k);
	}
	pi query(int k,int ql,int qr){
		if(rs<ql || ls>qr) return make_pair(-1,-1);
		if(ql<=ls && rs<=qr) return make_pair(tr[k].u,tr[k].v);
		if(qr<=mid) return query(k<<1,ql,qr);
		else if(ql>mid) return query(k<<1|1,ql,qr);
		else return merge(query(k<<1,ql,mid),query(k<<1|1,mid+1,qr));
	}
}
using namespace segtree;
int main(){
	int n=read();
	for(int x,y,z,i=1;i<n;i++){x=read(),y=read(),z=read();add(x,y,z);add(y,x,z);}
	dfs(1,0);init();
	build(1,1,n);
	int m=read();
	int a,b,c,d;
	while(m--){
		a=read(),b=read(),c=read(),d=read();
		pi t1=query(1,a,b);
		pi t2=query(1,c,d);
		cout<<max(max(ask(t1.first,t2.first),ask(t1.first,t2.second)),max(ask(t1.second,t2.first),ask(t1.second,t2.second)))<<"\n";
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值