首页 > 其他分享 >P3629 巡逻 LCA题解

P3629 巡逻 LCA题解

时间:2023-08-15 09:02:49浏览次数:46  
标签:路径 ed LCA 取反 st P3629 题解 operatorname 节点

原题:洛谷P3629

问题转化

首先,给定的图是一个有 \(n\) 个点,\(n-1\) 条边的无向连通图,这个图就等价于一棵树。

不建立新的道路时,从 \(1\) 号节点出发,把整棵树上的每条边遍历至少一次,再回到 \(1\) 号节点,会恰好经过每条边两次,路线总长度为 \(2(n-1)\),如下图最左边的部分所示。根据树的深度优先遍历的思想,很容易证明这个结论,因为每条边必然被递归一次、回溯一次。

建立 \(1\) 条新道路之后,因为新道路必须经过恰好一次(\(0\) 次、\(2\) 次都不可以),所以在沿着新道路 \((x,y)\) 巡逻之后,要返回 \(x\),就必须沿着树上从 \(y\) 到 \(x\) 的路径巡逻一遍,最终形成一个环。与不建立新道路的情况相结合,相当于树上 \(x\) 与 \(y\) 之间的路径就只需经过一次了,如上图所示。

因此,当 \(K=1\) 时,我们找到树的最长链,在两个端点之间加一条新道路,就能让总的巡逻距离最小。,答案就是 \(2(n-1)-l+1\)。

当 \(K=2\) 时,建立第 \(2\) 条新道路 \((u,v)\) 之后,又会形成一个环,若两条新道路形成的环不重叠,则树上 \(u,v\) 之间的路径只需经过一次,答案继续减小。否则,在两个环重叠的情况下,如果我们还按照刚才的方法把第 \(2\) 个环与建立 \(1\) 条新道路的情况相结合,两个换重叠的部分就不会被巡逻到,如下图所示。因为题目要求每条道路必须被巡逻,两个环重叠的部分就不会被巡逻到,并且返回。最终的结果应是两个环重叠的部分由 “只需经过一次” 变回了 “需要经过两次”。

综上所述,我们得到了如下算法。

\(1.\) 在最初的树上求直径,设直径为 \(l_1\)。然后把直径上的边权取反(从 \(1\) 改为 \(-1\))。

\(2.\) 在最长链边权取反后的树上再次求直径,设直径为 \(l_2\)。

答案就是 \(2(n-1)-(l_1-1)-(l_2)-1=2n-l_1-l_2\)。如果这条直径包含 \(l_1\) 取反的部分,就相当于两个环重叠。减掉 \((l_1-1)\) 后,重叠的部分变成了 “只需经过一次”,减掉 \((l_2-1)\) 后,相当于把重叠的部分加回来,变为 “需要经过两次”,与我们之前的讨论相符。时间复杂度为 \(O(n)\)。

以上摘编自李煜东《算法竞赛进阶指南》

处理 \(l_1\) 的路径

书中处理 \(l_1\) 的路径时用的是两次 \(\operatorname{BFS}\),而我们也可以用 \(\operatorname{LCA}\) 来处理,原理如下:

我们在处理 \(l_1\) 时可以同时处理出 \(l_1\) 的起点 \(st\) 和终点 \(ed\)。由于树上两点间的路径是唯一的,且这个路径一定经过这两点的 \(\operatorname{LCA}\)。所以用树形 \(\operatorname{DP}\) 处理完 \(l_1\) 后,求出 \(i\) 和 \(j\) 的 \(\operatorname{LCA}\),记为 \(x\)。然后先从 \(st\) 开始向父亲节点跳,跳到 \(x\),将期间经过的点顺序存进数组 \(road\)。再从 \(ed\) 开始跳,跳到 \(x\),将经过的点逆序存进数组 \(road\),数组 \(road\) 就是 \(l_1\) 的路径。

如何处理起点和终点呢?很简单,在树形 \(\operatorname{DP}\) 处理 \(D_i\) (从节点 \(i\) 出发走向以 \(i\) 为根的子树,能够到达的最远节点的距离)时,用 \(to_i\) 记录 从节点 \(i\) 出发走向以 \(i\) 为根的子树,能够到达的最远节点。那么对于由 \(D_i,D_j,(i,j)~(j∈son_i)\) 组成的链,它的起点和终点就是 \(to_i\) 和 \(to_j\)。

注意,对 \(to_i\) 的初值的处理应为:对于每个叶节点 \(u\),\(to_u=u\)。

还有一点要注意的,正常的存边方式无法通过起点和终点确定一条边,但是我们处理出的是路径上的点,只能通过起点和终点给边权取反,所以我们要改一下边权的存储方式:map<int,int>v[N],\(v_{ij}=w\) 表示从 \(i\) 到 \(j\) 有一条边权为 \(w\) 的边。

vector<int>e[N];
map<int,int>v[N];
inline void work()
{
	while(st!=x)	v[st][f[st][0]]=v[f[st][0]][st]=-1,st=f[st][0];
	while(ed!=x)	v[ed][f[ed][0]]=v[f[ed][0]][ed]=-1,ed=f[ed][0];
	//这里图省事,不把路径存数组里,在跳的时候直接把边权取反
}
void DP(int u,int num)
{
	vis[u]=1;
	if(du[u]==1)	to[u]=u;//判断叶节点
	for(reg int i=0;i<e[u].size();i=-~i)
	{
		if(vis[e[u][i]])	continue;
		DP(e[u][i],num);
		if(l[num]<d[u]+d[e[u][i]]+v[u][e[u][i]])	l[num]=d[u]+d[e[u][i]]+v[u][e[u][i]],st=to[u],ed=to[e[u][i]];
		if(d[u]<d[e[u][i]]+v[u][e[u][i]])	d[u]=d[e[u][i]]+v[u][e[u][i]],to[u]=to[e[u][i]];
	}
}
signed main()
{
	n=read();k=read();
	for(reg int i=1;i<n;i=-~i)
	{
		x=read();y=read();
		e[x].push_back(y);e[y].push_back(x);
		du[x]++;du[y]++;v[x][y]=v[y][x]=1;
	}
	DP(1,1);
	if(k==1)	return printf("%lld",2*(n-1)-l[1]+1),0;
	pre();dfs(1,0);x=lca(st,ed);//求lca,pre是预处理log
	work();
	memset(d,0,sizeof(d));
	memset(vis,0,sizeof(vis));//注意清零
	DP(1,2);
	return printf("%lld",2*n-l[1]-l[2]),0;
}

标签:路径,ed,LCA,取反,st,P3629,题解,operatorname,节点
From: https://www.cnblogs.com/sorato/p/17630358.html

相关文章

  • SVN 不显示红绿图标问题解决方案
    本地文件夹我们先在桌面或者资源管理器中鼠标右键打开设置  选择IconOverlays(图标覆盖) Status cache(状态缓存)选择‘Shell’ 接着选择IconOverlays(图标覆盖)下的IconSet(图标集)  选择应用然后确认,重启生效     ssh等方式挂载的远程磁盘......
  • 2019考研英语(一)小作文真题解析与参考 Aiding Rural Primary Schools 答复信
    2019考研英语(一)小作文真题解析与参考Directions:Supposeyouareworkingforthe“AidingRuralPrimarySchools” projectofyouruniversity.Writeanemailtoanswertheinquiryfromaninternationalschoolvolunteer,specifyingthedetailsoftheproject.Yous......
  • 2023NepCTF-RE部分题解
    2023NepCTF-RE部分题解九龙拉棺过反调试很容易发现void__stdcallsub_401700()里面有tea的痕迹接出来发现只是前半部分#include<stdio.h>#include<stdint.h>#include"defs.h"#include<stdio.h>#include<stdio.h>#include<stdint.h>//加密函数......
  • CodeForces-1798#B 题解
    正文开个数组\(last_k\)统计\(a_{i,j}\)最后买彩票的时间,再开一排桶\(day_t\)记录该天最后买彩票的有哪些人(即:有\(p\)满足\(last_p=t\)的集合)。将\(last_k\)放入\(day_t\)中,判断\(day_t\)中是否存在空桶,若有则无解(因为没有人在当天是最后买彩票的)。因为本题是......
  • 题解 CF379D New Year Letter
    思路提供一种比较容易想到的做法。拿到题看数据范围发现都很小,所以放心大胆地暴力。容易发现\(s_i\)中AC的个数等于\(s_{i-2}\)中AC的个数加\(s_{i-1}\)中AC的个数再加上两者拼接处可能有的一个AC。所以\(s_1\)和\(s_2\)从第二个字符到倒数第二个字符这之间......
  • P4412 题解
    P4412题解传送门更好的阅读体验简化题意:一张无向图,给定一棵生成树,求最小的修改边权的代价使得这棵生成树是最小生成树,代价定义为修改前后一条边的边权变化量的绝对值。思路首先,发现让这棵树成为最小生成树不好直接处理,但是判定是否为最小生成树却相对更容易。判定的思路......
  • CF1845D Rating System 题解
    题面给定一个长度为\(n\)数列\(a\),保证每项都不为\(0\)。初始时\(x=0\),然后对于\(1\lei\len\),按顺序进行如下操作:如果\(x\gek\),则\(x\rightarrow\max(k,x+a_i)\),否则\(x\rightarrowx+a_i\)。你需要求出\(k\),使得\(x\)的值尽量大。题解如果我们不考虑\(k......
  • Ynoi2001 冷たい部屋、一人 题解
    \(\text{link}\),这题太毒瘤啦!难写难调还略微卡常。谁爱卡常谁卡吧。反正我先贺为敬了。——引用自洛谷别人的提交记录本人写了两天(两个\(case\)各一天),调崩溃了才调出来,太毒瘤了!看到颜色相同发现不弱于\(O(n\sqrtn)\),一眼根号分治,设阈值为\(B\)。case1对于颜色出现次......
  • [ARC126C] Maximize GCD 题解
    题意给定一个序列\(A\),每次操作可以使\(A_i+1\)(\(i\in\left[1,n\right]\),\(K\)次操作的\(i\)可以不同),最多可以做\(K\)次。问\(\gcd{A_1,A_2,...,A_n}\)的最大值。题解首先,如果\(K\)可以把当前序列中所有的数都加到\(A_{\max}\),那就全部加到\(A_{\max}\),在......
  • [ABC215D] Coprime 2 题解
    题意给定数列\(A_N\)和一个正整数\(M\),求出所有的\(1\lek\leM\)满足\(\foralli\in\left[1,N\right],\gcd(k,A_i)=1\)。题解本题存在线性复杂度算法。记\(\operatorname{lpf}(n)=[1<n]\min\{p:p\midn\}+[1=n]\),即\(n\)的最小质因数。特别地,\(n......