NOIP2007 提高组] 树网的核 -(N^3,NlogN与ON三种解法)

AC不是我们的终极目标,我们的终极目标是获得经验。

题目大意:
一颗树,告诉你一个值的求法,让你在树的直径上选择一段路径使得这值最小

题目中的“值”:
在直径上选择一段路程F,这个值就是所有点到达F的最大值

首先介绍O(N^3)的解法:

适用于非加强版,数据范围N<=300
看到N<=300,我过去就是个flody嗷,什么dfs,树形dp求树的直径,lj

三层for之后
我们在任意两点之间选择最长的,并记录端点值
这时候树的直径和直径的两个端点就知道了
然后我们要在树的直径上选择一段路程F。
假设路程端点为pos1,pos2。 直径的端点为u,v,直径为maxlen
那么我们如何判断这个一段路程F是否在直径上?
那肯定是

f[pos1][pos2]+f[v][pos2]+f[u][pos1]==maxlen

在这里插入图片描述
就是这样,很简单吧
然后我们去求他题目中的值----偏心距
我们枚举路径F之外的点k
因为偏心距的定义是到F的最大值
这个点k到F的距离怎么求呢
那肯定是

t = max --> f[i][k]+f[j][k]-f[i][j])/2

然后再在所有的t中取一个最小值就ok了

O(NlogN)的解法:

最大值最小化–我过去就是一个二分嗷
老套路了

满足单调性:

简单再说下,如果答案mid作为最大值可以,那么mid+1一定也满足条件(说了跟没说一个样?

显然我们这里需要二分答案mid
我们考虑如何O(n)的check

和解法一一致:
假设路程端点为pos1,pos2。 直径的端点为u,v,直径为maxlen

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210315000455732.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dteTA1MzY=,size_16,color_FFFFFF,t_70

假设图是这样的
假设我们选出的路径是F
那么我们需要对F上的每个点都dfs一遍求得一个max
但是我们之前说过
要在O(n)的时间复杂度呢check
也就是说,每个点只走一次

我们发现:

在直径u-v这条链上的所有的点除了端点。都不会对答案产生贡献
什么意思呢?

我们看k这个点,他到路径F的距离会被直径的左端点代替,因为到左端点更大

对于直径这条链上的所有的点(除了端点)都是如此
那么我们只需要扫支链就可以了
枚举pos1到pos2之间的每个点,求到所有支链上的最大值,如果大于mid,就是不行的

好了,时间复杂度我们能够保证了

还有个小的问题就是pos1和pos2的位置如何确定?

	首先我们要保证pos1和pos2之间的距离小于等于s(限制条件)

看上面那张图

根据直径的最长性,任何从 u, p 之间分叉离开直径的子树,其最远点与 p 的距离都不会比 u 更远。所以 p, q就是在满足直径两侧的那部分节点偏心距不超过 mid 的前提下,尽量靠近树网中心的节点。

剩下的就是一些细节方面的操作了

O(n)的解法:

经过上面两种解法的熏陶
我们直接快进好吧
快进到 知道树的直径的长度和一个端点

我们分为两种情况讨论:

一 直径的长度小于等于限制长度

在这里插入图片描述

还拿这张图来说
因为直径的长度小于等于限制的长度
所以我直径上任意取两个点pos1,pos2都能够满足条件
这个时候对答案产生贡献的只有端点,因为到支链的点的长度一定小于到端点的(不然直径的两端点就换了)
如果我pos1 ,pos2取在图上的位置
然后对两端点取一个max
这个时候就不如这样(下图)
在这里插入图片描述
仔细一想,我直接把pos1和pos2直接取到端点不就行了嘛,这样ans就等于0 了啊。
但是答案肯定不是0啊,应该是支链上的某一点到路径F的距离了
我们此时把整条直径都标记起来,然后跑一遍支链的所有的点到直径的距离,直接取一个最大就是答案

二 一直径的长度大于限制长度

此时,我们不能像做法一一样完全的标记直径
所以要尺取不会证明正确性 )但是感觉就是可以
然后对答案产生贡献的还是端点(上面说过了)
这个时候对两个端点的贡献取一个max
然后再跟答案取一个min就ok了

有时候可以用一个dfs函数实现的功能我给分开写,所以代码略显冗余

Code_n^3:

int n,f[666][666],s;
int main() {
	ios::sync_with_stdio(false);
	cin>>n>>s;
	rep(i,1,n) rep(j,1,n) f[i][j] = 1e7;
	rep(i,1,n) f[i][i] =0;
	for(int i=1 ; i<=n-1 ; i++) {
		int  u,v,w;
		cin>>u>>v>>w;
		f[u][v] = f[v][u] = w;
	}
	rep(k,1,n) rep(i,1,n) rep(j,1,n) f[i][j] = min(f[i][j],f[i][k]+f[k][j]);
	int pos1,pos2,maxlen = -1;
	for(int i=1 ; i<=n ; i++) {
		for(int j=1 ; j<=n ; j++) {
			if(f[i][j]>maxlen) {
				pos1 = i,pos2 = j,maxlen  = f[i][j];
			}
		}
	}

	int ans = 1e7;
	for(int i=1 ; i<=n ; i++) {
		for(int j=1 ; j<=n ; j++) {
			if(f[i][j]+f[j][pos2]+f[i][pos1]!=maxlen) continue;
			if(f[i][j]>s) continue;
			int temp  =-1;
			for(int k=1; k<=n ; k++) {
				temp = max(temp,(f[i][k]+f[j][k]-f[i][j])/2  );
			}
			ans = min(ans,temp);
		}
	}
	cout<<ans;

Code_nlogn:

int  head[maxn],cnt;
struct node {
	int u,v,w,next;
} e[maxn];
void add(int u,int v,int w) {
	e[cnt].u=u,e[cnt].v=v,e[cnt].w=w;
	e[cnt].next=head[u],head[u]=cnt++;
}
int n,s,pos,mi,vis[maxn],fa[maxn],dist[maxn];
void dfs(int u,int p) {
	fa[u] = p;
	if(dist[u]>mi) mi = dist[u],pos = u;
	for(int i=head[u]; ~i; i=e[i].next) {
		int v=e[i].v;
		if(v==p) continue;
		dist[v] = dist[u] + e[i].w;
		dfs(v,u);
	}
}
int ma = -1,dis[maxn];
void  dfs_max(int u,int p) {
	ma = max(ma, dis[u]);
	for(int i=head[u]; ~i; i=e[i].next) {
		int v=e[i].v;
		if(v==p||vis[v]) continue;
		dis[v] = dis[u] + e[i].w;
		dfs_max(v,u);
	}
}
int ok(int x) {

	int pos1=0 ,pos2 =0 ;
	for(int i = pos ; i ; i = fa[i]) {
		if(dist[pos] - dist[i]<=x) pos1 = i;
		else break;
	}

	for(int i = pos ; i ; i = fa[i]) {
		if(dist[i]<=x) {
			pos2 = i;
			break;
		}
	}

	mst(dis,0);
	if( dist[pos1] - dist[pos2]>s) return 0;
	else if(dist[pos1] - dist[pos2]<=0) {
		for(int i=pos1 ; i ; i=fa[i]) {
			ma =-1;
			dis[i]=0 ;
			dfs_max(i,fa[i]);
			if(ma>x) return 0;
		}
	} else {
		for(int i=pos1 ; i!=fa[pos2] ; i=fa[i]) {
			ma =-1;
			dis[i]=0 ;
			dfs_max(i,fa[i]);
			if(ma>x) return 0;
		}
	}

	return 1;
}
int main() {
	ios::sync_with_stdio(false);
	cin>>n>>s;
	memset(head,-1,sizeof head);
	for(int i=1 ; i<=n-1 ; i++) {
		int u,v,w;
		cin>>u>>v>>w;
		add(u,v,w),add(v,u,w);
	}
	dfs(1,0);
	dist[pos]=0,mi=0;
	dfs(pos,0);
	for(int i=pos ; i ; i=fa[i]) vis[i] = 1;
	int l=0,r=2e9,ans;
	while(l<=r) {
		int mid = (l+r)>>1;
		if(ok(mid)) r = mid-1,ans = mid;
		else l = mid+1;
	}
	cout<<ans;
	return 0;
}
/*

*/

Code_n:

int head[maxn],cnt;
struct node {
	int u,v,w,next;
} e[maxn];
void add(int u,int v,int w) {
	e[cnt].u=u,e[cnt].v=v,e[cnt].w=w;
	e[cnt].next=head[u],head[u]=cnt++;
}
int n,dist[maxn],pos1,pos2,mi = -1,s,fa[maxn],ans = inf;
int vis[maxn];
void dfs1(int u,int p,int step) {
	if(step>mi) pos1 = u,mi = step;
	for(int i=head[u]; ~i; i=e[i].next) {
		int v  = e[i].v;
		if(v==p) continue;
		dfs1(v,u,step+e[i].w);
	}
}
void dfs2(int u,int p) {
	fa[u] = p;
	if(dist[u]>mi)  mi = dist[u],pos2=u;
	for(int i=head[u]; ~i; i=e[i].next) {
		int v  = e[i].v;
		if(v==p) continue;
		dist[v] = dist[u]+e[i].w;
		dfs2(v,u);
	}
}
void dfs3(int u,int p) {
	for(int i=head[u]; ~i; i=e[i].next) {
		int v  = e[i].v;
		if(v==p||vis[v]) continue;
		dist[v] = dist[u]+e[i].w;
		dfs3(v,u);
	}
}
int main() {
	n=read(),mst(head,-1),s=read();
	for(int i=1 ; i<n ; i++) {
		int u,v,w;
		u=read(),v=read(),w=read();
		add(u,v,w),add(v,u,w);
	}
	dfs1(1,1,0),mi=-1;
	dfs2(pos1,-1);


	for(int i = pos2,j = pos2 ; ~i ; i = fa[i]) {
		while(dist[i] - dist[j]<=s&&~j&&dist[i]-dist[j]>=0) {
			int t = max(dist[j],dist[pos2] - dist[i]);
			ans = min(ans,t);

			j = fa[j];
		}
	}
	if(dist[pos2]>s) {
		out(ans);
		return 0;
	} else {
		int ans = -1;
		for(int i = pos2 ; ~i ; i = fa[i]) vis[i] = 1;
		for(int i = pos2 ; ~i ; i = fa[i]) dist[i] =0, dfs3(i,fa[i]);
		rep(i,1,n)  ans = max(ans,dist[i]);
		out(ans);
	}

	return 0;

}
/*

*/
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
要在NOIP提高组复赛(CSP-S第二轮)中成绩列全国前20%,需要做出以下几点努力。 首先,我会全面掌握每一道题目的考点和解题技巧。通过仔细研究历年的试题和参考书籍,理解各种算法和数据结构的原理和应用,提高自己的解题能力和代码写作水平。我会注重对动态规划、图论、数论、搜索等常见题型进行深入学习和理解,提高解题的效率和准确性。 其次,我会参加相关的竞赛和训练,提升自己的编程能力和思维能力。通过参加线下和线上的编程竞赛,与优秀的选手相互学习和切磋,不断提高自己的解题速度和思考问题的能力。在训练过程中,我会结合《算法竞赛入门经典》等参考书籍,进行有针对性的训练和复习,加深对算法和题型的理解。 此外,我也会注重团队合作和经验交流。与队友共同讨论题目的解法和优化思路,相互帮助和学习。积极参加讲座、讨论会和交流活动,与其他选手互动,分享经验和解题思路,拓宽自己的视野和思路。 最后,我会不断总结经验,总结自己在训练和比赛中的不足和问题。通过反思和反馈,找出提升的空间和改进的方法,不断调整和改进自己的学习和训练方案。同时,也要保持耐心和毅力,面对困难和挑战时不轻易放弃,不断提升自己的解题能力和竞赛经验,争取在NOIP提高组复赛中获得好的成绩,进入全国前20%。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值