51nod 1766 树上的最远点对

基准时间限制:3 秒 空间限制:524288 KB 分值: 80  难度:5级算法题
 收藏
 关注
n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c<=j<=d}
(PS 建议使用读入优化)
Input
第一行一个数字 n n<=100000。
第二行到第n行每行三个数字描述路的情况, x,y,z (1<=x,y<=n,1<=z<=10000)表示x和y之间有一条长度为z的路。
第n+1行一个数字m,表示询问次数 m<=100000。
接下来m行,每行四个数a,b,c,d。
Output
共m行,表示每次询问的最远距离
Input示例
5
1 2 1
2 3 2
1 4 3
4 5 4
1
2 3 4 5
Output示例
10
xfause  (题目提供者)



这题需要一个结论

设集合A中的最远点对为u,v

集合B中的最远点对为p,q

那么集合 A并B 的最远点对为u,v,p,q中的两个(即C(4,2)==6)

证明类似树的直径的证明

然后用欧拉序RMQ求lca+线段树+暴力合并就可以过这道题了

我rank16!!!

注意询问时只有4种情况(没有(u,v)和(p,q))(pushup和pushup_ans的区别)

其实我的代码加个函数可以简洁很多的

但是直接复制过来也不错QAQ

——————————分割线———————————————

突然发现我的lca写错了QAQ

应该是欧拉序中深度最小的点

而不是编号最小的。。。。。。

然而我竟然A了。。。。。

代码我懒得改了

大家自己注意啊QAQ

#include<cstdio>
#include<cstring>
inline int read()
{
	int ans=0,f=1;char t=getchar();
	while(t<'0'||t>'9')	f=(t=='-'?-1:1),t=getchar();
	while(t>='0'&&t<='9')	ans=ans*10+t-'0',t=getchar();
	return ans*f;
}
const int N=1e5+7;
struct node
{
	int to,next,c;
}e[N<<1];
int first[N];int cnt;
inline void insert(int u,int v,int c)
{
	e[++cnt]=(node){v,first[u],c};first[u]=cnt;
	e[++cnt]=(node){u,first[v],c};first[v]=cnt;
}
int qu[N*2],visit[N];
int tar[N];
int dis[N];
void dfs(int x)
{
	qu[++cnt]=x;tar[x]=cnt;visit[x]=1;
	for(int k=first[x];k;k=e[k].next)
	if(!visit[e[k].to])
	{
		dis[e[k].to]=dis[x]+e[k].c;
		dfs(e[k].to);
		qu[++cnt]=x;
	}
}
int lg[N*2+8];
int f[N*2+8][25];
struct edgt
{
	int l,r;int u,v;
	int dis;
}tr[N*4];
#define lson ro<<1
#define rson ro<<1|1
const int inf=2*1e9+7;
inline int min(int u,int v)
{	
	return u<v?u:v;
}
inline void swap(int &a,int &b)
{
	int tmp=a;
	a=b,b=tmp;
}
inline int lca(int l,int r)
{
	l=tar[l],r=tar[r];
	if(l>r)	swap(l,r);
	int t=lg[r-l+1];
	return min(f[l][t],f[r-(1<<t)+1][t]);
}
inline int querydis(int u,int v)
{
	return dis[u]+dis[v]-(dis[lca(u,v)]<<1);
}
inline void pushup_ans(edgt &ro,edgt l,edgt r)
{
	bool tag1=( (l.l==l.r) &&l.l>0),tag2=( (r.l==r.r) && (r.l>0) );
	if(tag1&&!tag2)
	{
		int ans=0,i=0,j=0,tt=0;
		
		tt=querydis(l.u,r.u);
		if(tt>ans)	ans=tt,i=l.u,j=r.u;
		
		tt=querydis(l.u,r.v);
		if(tt>ans)	ans=tt,i=l.u,j=r.v;
		
		ro.dis=ans,ro.u=i,ro.v=j;
	}
	else if(!tag1&&tag2)
	{
		int ans=0,i=0,j=0,tt=0;
		
		tt=querydis(l.u,r.u);
		if(tt>ans)	ans=tt,i=l.u,j=r.u;
		
		tt=querydis(l.v,r.u);
		if(tt>ans)	ans=tt,i=l.v,j=r.u;
		ro.dis=ans,ro.u=i,ro.v=j;
	}
	else if(tag1&&tag2)
	{
		ro.u=l.l;ro.v=r.l;
		ro.dis=querydis(ro.u,ro.v); 
	}
	else 
	{
		int ans=0,i=0,j=0,tt=0;
		tt=querydis(l.u,r.u);
		if(tt>ans)	ans=tt,i=l.u,j=r.u;
		
		tt=querydis(l.u,r.v);
		if(tt>ans)	ans=tt,i=l.u,j=r.v;
		
		tt=querydis(l.v,r.u);
		if(tt>ans)	ans=tt,i=l.v,j=r.u;
		
		tt=querydis(l.v,r.v);
		if(tt>ans)	ans=tt,i=l.v,j=r.v;
		
		ro.dis=ans,ro.u=i,ro.v=j;
	}
}
inline void pushup(edgt &ro,edgt l,edgt r)
{
	bool tag1=( (l.l==l.r) &&l.l>0),tag2=( (r.l==r.r) && (r.l>0) );
	if(tag1&&!tag2)
	{
		int ans=0,i=0,j=0,tt=0;
		if(r.dis>ans)	ans=r.dis,i=r.u,j=r.v;
		
		tt=querydis(l.u,r.u);
		if(tt>ans)	ans=tt,i=l.u,j=r.u;
		
		tt=querydis(l.u,r.v);
		if(tt>ans)	ans=tt,i=l.u,j=r.v;
		
		ro.dis=ans,ro.u=i,ro.v=j;
	}
	else if(!tag1&&tag2)
	{
		int ans=0,i=0,j=0,tt=0;
		if(l.dis>ans)	ans=l.dis,i=l.u,j=l.v;
		
		tt=querydis(l.u,r.u);
		if(tt>ans)	ans=tt,i=l.u,j=r.u;
		
		tt=querydis(l.v,r.u);
		if(tt>ans)	ans=tt,i=l.v,j=r.u;
		ro.dis=ans,ro.u=i,ro.v=j;
	}
	else if(tag1&&tag2)
	{
		ro.u=l.l;ro.v=r.l;
		ro.dis=querydis(ro.u,ro.v); 
	}
	else 
	{
		int ans=0,i=0,j=0,tt=0;
		if(l.dis>ans)	ans=l.dis,i=l.u,j=l.v;
		if(r.dis>ans)	ans=r.dis,i=r.u,j=r.v;
		tt=querydis(l.u,r.u);
		if(tt>ans)	ans=tt,i=l.u,j=r.u;
		
		tt=querydis(l.u,r.v);
		if(tt>ans)	ans=tt,i=l.u,j=r.v;
		
		tt=querydis(l.v,r.u);
		if(tt>ans)	ans=tt,i=l.v,j=r.u;
		
		tt=querydis(l.v,r.v);
		if(tt>ans)	ans=tt,i=l.v,j=r.v;
		
		ro.dis=ans,ro.u=i,ro.v=j;
	}
}
void build(int ro,int l,int r)
{
	tr[ro].l=l,tr[ro].r=r;
	if(l==r)
	{	
		tr[ro].u=l,tr[ro].v=l;
		tr[ro].dis=-inf;
		return;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);build(rson,mid+1,r);
	pushup(tr[ro],tr[lson],tr[rson]);
}
edgt query(int ro,int l,int r)
{
	if(l<=tr[ro].l&&tr[ro].r<=r)		return tr[ro];
	int mid=(tr[ro].l+tr[ro].r)>>1;
	if(r<=mid)	return query(lson,l,r);
	else if(l>mid)	return query(rson,l,r);
	else
	{
		edgt ans;
		edgt a=query(lson,l,mid),b=query(rson,mid+1,r);
		pushup(ans,a,b);
		return ans;
	}
}
int main()
{
	int n=read();
	int u,v,c;
	for(int i=1;i<n;i++)	u=read(),v=read(),c=read(),insert(u,v,c);
	cnt=0;
	dfs(1);	
	
	lg[1]=0;
	for(int i=2;i<=cnt;i++)		lg[i]=lg[i>>1]+1;	
	for(int i=1;i<=cnt;i++)	f[i][0]=qu[i];
	
	for(int j=1;j<=22;j++)
	for(int i=1;i<=cnt-( 1<<(j -1) );i++)	
	f[i][j]=min(f[i][j-1],f[ i+( 1<< (j-1) )][ j-1 ]);
	
	int p,q;
	build(1,1,n);	
	int m;
	m=read();
	for(int i=1;i<=m;i++)
	{
		u=read(),v=read(),p=read(),q=read();
		edgt a=query(1,u,v);
		edgt b=query(1,p,q);
		edgt ans;
		pushup_ans(ans,a,b);
		printf("%d\n",ans.dis);
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值