3824. 【NOIP2014模拟9.9】渴

3824. 【NOIP2014模拟9.9】渴

Description

给定一个 nnn 个点的树,后有 mmm 次询问,每次给出两个点 x,yx,yx,y ,问 max⁡(min⁡(disi,x,disi,y))i∈[1,n]andi≠x,y\max(\min( dis_{i,x} ,dis_{i,y} )) i \in [1,n] and i \not= x,ymax(min(disi,x,disi,y))i∈[1,n]andi=x,y


Solution

小范围内暴力做,大范围内直接拿直径端点判

按理来说大范围的乱搞虽然是错的,但是链的分应该拿到啊。

很奇怪反正。

后来想想如果 x,yx,yx,y 两个点都偏向端点,那么最远的点可能是中间的,所以会错。要把链的分拿到还是要单独拿出来打。

其实是好做的只是当时想简单了,痛失 10 pts 啊。

现在会了

简单写一写吧

首先,到某个点最长的路径一定与树的直径部分重叠,因为最远的点一定是直径的某个端点。

那么我们首先找出树的直径,再从直径的每个点出发 dfs ,要求不经过直径上的其他点,这样就可以得出其他点到直径上哪个点(指在直径上第几个)最近,以及距离。

现在我们输入两个数 x,yx,yx,y ,令 xxx 离直径上的第 tpxtpxtpx 个点最近,他们之间的距离为 dsxdsxdsx ( yyy 同理)。

(这里默认 tpx<tpytpx<tpytpx<tpy )

那么现在我们可以找到从 x∼yx \sim yx∼y 的一条路径(实际上在树上这是唯一的),也知道这条路径经过直径的部分( tpx∼tpytpx \sim tpytpx∼tpy )。算出这条路径中点在直径上的位置 mid=tpx+tpy+dsy−dsx2mid=\frac{tpx+tpy+dsy-dsx}{2}mid=2tpx+tpy+dsy−dsx (一定在直径上),实际上在 midmidmid 左边的点一定离 xxx 更近,另一边离 yyy 更近。

现在分四种情况讨论最远的点所在的位置:

  1. 直径的左端点
  2. 直径的右端点
  3. 在直径上 tpx∼midtpx \sim midtpx∼mid 的这一段中,某一点 iii 向下延申找到一个极深点,向上走回直径,走直径上第 i∼tpxi \sim tpxi∼tpx ,再走到 xxx
  4. 在直径上 mid∼tpymid \sim tpymid∼tpy 的这一段中,某一点 iii 向下延申找到一个极深点,向上走回直径,走直径上第 i∼tpyi \sim tpyi∼tpy ,再走到 yyy

前两种情况是好做的,后两种需要一些处理。

我们现在需要求的就是直径上某一段向下最深的点,其实这个是从头到尾不会改变的,可以考虑使用 st 表维护。

发现我们在前面将直径上每个点作为起点 dfs 的时候,可以顺势求出在它可以遍历到的范畴内离它最远的点,记直径上第 iii 个点向下遍历最深的点到直径左端点的距离为 f0,if_{0,i}f0,i ,到右端点则为 g0,ig_{0,i}g0,i 。采用端点是因为这样比较的基准是一样的。

然后前面的东西我们就可以完成啦。

注意判断 x,yx,yx,y 两点在到直径最近的点是同一个的情况。


Code

c++ 复制代码
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int N=2e5,LGN=log2(N);
struct edge
{
	int to,nxt;
}e[2*N+5];
int lst[N+5],cnt;
int dep[N+5],fa[N+5];
int tp[N+5];
bool bz[N+5];
int a[N+5];
int f[2][LGN+5][N+5];
int dmx;
int n,lgn;
void ad(int x,int y)
{
	e[++cnt]=(edge){y,lst[x]};
	lst[x]=cnt;
}
void dfs(int x,int y,int z)
{
	dep[x]=dep[y]+1,fa[x]=y,tp[x]=z;
	if(dep[x]>dep[dmx])
	{
		dmx=x;
	}
	for(int i=lst[x];i;i=e[i].nxt)
	{
		if((e[i].to!=y)&&(!bz[e[i].to]))
		{
			dfs(e[i].to,x,z);
		}
	}
}
void st()
{
	int lg=log2(a[0]);
	for(int i=1;i<=lg;i++)
	{
		for(int j=1;(j+(1<<i)-1)<=a[0];j++)
		{
			for(int k=0;k<=1;k++)
			{
				f[k][i][j]=max(f[k][i-1][j],f[k][i-1][j+(1<<(i-1))]);
			}
		}
	}
}
int qry(int opt,int l,int r)
{
	if(l>r)
	{
		return 0;
	}
	int t1=log2(r-l+1);
	return max(f[opt][t1][l],f[opt][t1][r-(1<<t1)+1]);
}
int main()
{
//	freopen("thirst1.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		ad(u,v),ad(v,u);
	}
	dfs(1,0,0);
	int de1=dmx;
	dmx=0;
	dfs(de1,0,0);
	int de2=dmx;
	for(int i=de2;i!=de1;i=fa[i])
	{
		bz[i]=1,a[++a[0]]=i;
	}
	bz[de1]=1,a[++a[0]]=de1;
	for(int i=1;i<=a[0];i++)
	{
//		printf(" %d %d\n",i,a[i]);
		dmx=0;
		dfs(a[i],0,i);
		f[0][0][i]=i-1+dep[dmx]-1,f[1][0][i]=a[0]-i+dep[dmx]-1;
	}
//	printf("%d %d %d\n",de1,de2,a[0]);
	st();
	int m;
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		int tpx=tp[x],tpy=tp[y];
		if(tpx>tpy)
		{
			swap(tpx,tpy),swap(x,y);
		}
		int dsx=dep[x],dsy=dep[y];
//		printf(" %d %d %d %d\n",tpx,tpy,dsx,dsy);
		int mid=(tpx+tpy+dsy-dsx)/2;
		printf("%d\n",(tpx==tpy)?(max(tpx-1,a[0]-tpx)+min(dsx,dsy)-1):max(max(min(dsx-1+tpx-1,dsy-1+tpy-1),min(dsx-1+a[0]-tpx,dsy-1+a[0]-tpy)),max(qry(0,tpx+1,mid)+dsx-1-(tpx-1),qry(1,mid+1,tpy-1)+dsy-1-(a[0]-tpy))));
	}
	return 0;
}

Thanks

码字不易,请多支持😘

如有疏漏,欢迎评论区留言或者私信,我会及时调整更正。

谢谢啦🍝

相关推荐
gorgeous(๑>؂<๑)1 小时前
【ICLR26-Oral Paper】透过对比的视角:视觉语言模型中的自改进视觉推理
人工智能·算法·语言模型·自然语言处理
AC赳赳老秦2 小时前
软件组件自动化的革命:DeepSeek 引领高效开发新时代
运维·人工智能·算法·云原生·maven·devops·deepseek
小亮✿2 小时前
并查集OJ做题报告
算法·个人知识总结·做题报告
ShineWinsu2 小时前
对于模拟实现C++list类的详细解析—上
开发语言·数据结构·c++·算法·面试·stl·list
Mr YiRan2 小时前
C++语言类中各个重要函数原理
java·开发语言·c++
程序员酥皮蛋2 小时前
hot 100 第二十九题 29.删除链表的倒数第 N 个结点
数据结构·算法·leetcode·链表
stripe-python2 小时前
十二重铲雪法(下)
c++·算法
I Promise342 小时前
BEV视角智驾方案全维度发展梳理
人工智能·算法·计算机视觉
D_evil__2 小时前
【Effective Modern C++】第五章:右值引用、移动语义和完美转发:29. 认识移动操作的缺点
c++