首页 > 其他分享 >[题解]P9432 [NAPC-#1] rStage5 - Hard Conveyors

[题解]P9432 [NAPC-#1] rStage5 - Hard Conveyors

时间:2024-06-10 20:22:16浏览次数:22  
标签:-# mindis min int 题解 Hard fa minn ans

P9432 [NAPC-#1] rStage5 - Hard Conveyors

题意简述

给定一个\(N\)个节点的树形结构,其中有\(k\)个关键节点。

接下来有\(q\)次询问,每次询问给定\(x,y\),请输出\(x\)到\(y\)至少经过一个关键点的最短路径。

解题思路

我们发现,这道题相当于让我们从\(x\)到\(y\)的简单路径上,额外扩展出一路程去到达一个关键点。那么从哪里开始,到达哪一个关键点能消耗最少的步数呢?

我们不难想到,可以定义\(mindis[i]\)表示\(i\)到最近关键点的距离。这样答案就是:

\[2\times \min\limits_{i是x\sim y路径上的节点}(mindis[i])+dist(x,y) \]

先考虑\(mindis\)怎样计算。我们可以巧妙地用Dijkstra来求(只需要在堆优化Dijkstra的基础上稍作修改即可:初始化时,把所有关键点距离设为\(0\),并将它们入队列)。这一步骤时间复杂度\(O(n\log n)\)。

但我们还可以用\(O(n)\)的方法求出。\(mindis\)初始化为\(+\infty\),过程分为\(2\)次DFS。

  • 第\(1\)次DFS:
    • 如果\(u\)是关键点,那么\(mindis[u]=0\)。
    • 给子节点\(i\)赋初值\(mindis[i]=mindis[u]+w(u,i)\)(\(w\)表示边权),并搜索子节点。
    • 接着用子节点\(i\)的值更新\(u\):\(mindis[u]=\min(mindis[u],mindis[i]+w(u,i))\)。
  • 第\(2\)次DFS:
    • 第\(1\)次DFS可能会导致一些节点的\(mindis\)没有被更新,此时我们需要用它们父节点最终的\(mindis\)来更新子节点:即\(mindis[i]=\min(mindis[i],mindis[u]+w(u,i))\)。
    • 搜索子节点,重复上述步骤。

\(mindis\)求出来了,但如果我们用\(O(n)\)的复杂度去遍历\(x\sim y\)路径上的所有节点,就会超时。

我们可以用倍增的思想来优化这一过程。用\(minn[i]\)来表示每个点往上\(2^i\)层的\(mindis\)。因为待会求\(dist(x,y)\)需要用LCA,所以\(minn\)就在预处理LCA的时候一块计算出来。

求\(dist(x,y)\)的话,用\(dis[u]\)表示\(u\)到根节点的距离(预处理LCA时计算出来),答案就是\(dis[x]+dis[y]-dis[lca(x,y)]\)。

怎么求\(\min\limits_{i是x\sim y路径上的节点}(mindis[i])\)呢?我们仍然用倍增的思想,让\(x,y\)往上跳,每跳一次用\(minn\)更新一下最小的\(mindis\),和求LCA的方法十分相似,所以就和求\(dist(x,y)\)合并成一个函数\(lca(x,y)\)了。返回值是一个pairfirst是LCA,second是最小的\(mindis\)。

用上面的式子得出结果即可,别忘了\(\times 2\)。

两种\(mindis\)求法的时间复杂度都是\(O((n+q)\log N)\),但显然DFS求法更高效。

实现细节:

  • 如果用\(O(n)\)的方法求\(mindis\),别忘了距离数组初始化为\(+\infty\),但别太大,因为有\(+1\)操作。

Code

DFS求$mindis$
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define N 100010
using namespace std;
int n,q,k,dis[N],mindis[N];
//dis表示到根的距离
//mindis表示到关键点的最小距离
//minn维护每个点往上2^x层的mindis
int dep[N],fa[N][20],minn[N][20];
bool is[N],vis[N];
struct edge{int to,w;};
vector<edge> G[N];
void dfs1(int u,int father){
	if(is[u]) mindis[u]=0;
	for(auto i:G[u]){
		if(i.to==father) continue;
		mindis[i.to]=mindis[u]+i.w;
		dfs1(i.to,u);
		mindis[u]=min(mindis[u],mindis[i.to]+i.w);
	}
}
void dfs2(int u,int father){
	for(auto i:G[u]){
		if(i.to==father) continue;
		mindis[i.to]=min(mindis[i.to],mindis[u]+i.w);
		dfs2(i.to,u);
	}
}
void dfs(int u,int father){
	dep[u]=dep[father]+1;
	fa[u][0]=father;
	for(int i=1;i<20;i++)
		fa[u][i]=fa[fa[u][i-1]][i-1],
		minn[u][i]=min(minn[u][i-1],minn[fa[u][i-1]][i-1]);
	for(auto i:G[u])
		if(i.to!=father)
			minn[i.to][0]=min(mindis[i.to],mindis[u]),
			dis[i.to]=dis[u]+i.w,
			dfs(i.to,u);
}
pair<int,int> lca(int u,int v){
	//first:LCA  second:minn
	if(u==v) return {u,mindis[u]};
	int ans=INT_MAX;
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=19;i>=0;i--)
		if(dep[fa[u][i]]>=dep[v]){
			ans=min(ans,minn[u][i]),u=fa[u][i];
		}
	if(u==v) return {u,ans};
	for(int i=19;i>=0;i--)
		if(fa[u][i]!=fa[v][i])
			ans=min(ans,min(minn[u][i],minn[v][i])),
			u=fa[u][i],v=fa[v][i];
	ans=min(ans,min(minn[u][0],minn[v][0]));
	return {fa[u][0],ans};
}
int solve(int u,int v){
	auto t=lca(u,v);
	return dis[u]+dis[v]-2*dis[t.first]+2*t.second;
}
signed main(){
	memset(mindis,0x7f,sizeof mindis);
	cin>>n>>q>>k;
	for(int i=1;i<n;i++){
		int u,v,w;
		cin>>u>>v>>w;
		G[u].push_back({v,w});
		G[v].push_back({u,w});
	}
	for(int i=1;i<=k;i++){
		int u;
		cin>>u;
		is[u]=1;
	}
	dfs1(1,0);
	dfs2(1,0);
	dfs(1,0);
	while(q--){
		int x,y;
		cin>>x>>y;
		cout<<solve(x,y)<<"\n";
	}
	return 0;
}
Dijkstra求$mindis$
#include<bits/stdc++.h>
#define int long long
#define PII pair<int,int>
#define N 100010
using namespace std;
int n,q,k,dis[N],mindis[N];
//dis表示到根的距离
//mindis表示到关键点的最小距离
//minn维护每个点往上2^x层的mindis
int dep[N],fa[N][20],minn[N][20];
bool is[N],vis[N];
struct edge{int to,w;};
vector<edge> G[N];
void dijkstra(){
	priority_queue<PII,vector<PII>,greater<PII>> heap;
	while(!heap.empty()) heap.pop();
	for(int i=1;i<=n;i++){
		if(!is[i]) mindis[i]=INT_MAX;
		else heap.push({0,i});
	}
	while(!heap.empty()){
		auto u=heap.top().second;
		heap.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(auto i:G[u]){
			if(mindis[u]+i.w<mindis[i.to]){
				mindis[i.to]=mindis[u]+i.w;
				heap.push({mindis[i.to],i.to});
			}
		}
	}
}
void dfs(int u,int father){
	dep[u]=dep[father]+1;
	fa[u][0]=father;
	for(int i=1;i<20;i++)
		fa[u][i]=fa[fa[u][i-1]][i-1],
		minn[u][i]=min(minn[u][i-1],minn[fa[u][i-1]][i-1]);
	for(auto i:G[u])
		if(i.to!=father)
			minn[i.to][0]=min(mindis[i.to],mindis[u]),
			dis[i.to]=dis[u]+i.w,
			dfs(i.to,u);
}
pair<int,int> lca(int u,int v){
	//first:LCA  second:minn
	if(u==v) return {u,mindis[u]};
	int ans=INT_MAX;
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=19;i>=0;i--)
		if(dep[fa[u][i]]>=dep[v]){
			ans=min(ans,minn[u][i]),u=fa[u][i];
		}
	if(u==v) return {u,ans};
	for(int i=19;i>=0;i--)
		if(fa[u][i]!=fa[v][i])
			ans=min(ans,min(minn[u][i],minn[v][i])),
			u=fa[u][i],v=fa[v][i];
	ans=min(ans,min(minn[u][0],minn[v][0]));
	return {fa[u][0],ans};
}
int solve(int u,int v){
	auto t=lca(u,v);
	return dis[u]+dis[v]-2*dis[t.first]+2*t.second;
}
signed main(){
	cin>>n>>q>>k;
	for(int i=1;i<n;i++){
		int u,v,w;
		cin>>u>>v>>w;
		G[u].push_back({v,w});
		G[v].push_back({u,w});
	}
	for(int i=1;i<=k;i++){
		int u;
		cin>>u;
		is[u]=1;
	}
	dijkstra();
	dfs(1,0);
	while(q--){
		int x,y;
		cin>>x>>y;
		cout<<solve(x,y)<<"\n";
	}
	return 0;
}

标签:-#,mindis,min,int,题解,Hard,fa,minn,ans
From: https://www.cnblogs.com/Sinktank/p/18240932

相关文章

  • Python数据框操作 -- 删除数据(去除空值或者特定值)
    先创建一个数据框:importpandasaspddf=pd.DataFrame({'a':[1,1,np.nan,np.nan,4],'b':[5,6,np.nan,8,np.nan]})删除特定值存在的行数据框删去特定值所在行:df1=df.drop(df[df['a']==4].index,inplace=True) 删除存在空值的行删除有空值的所有行:df1=df.dr......
  • 二叉树的所有路径-力扣
    这道题目需要返回给定二叉树所有从根节点到叶子节点的路径,那么对二叉树进行深度优先搜索,遇到节点就将其加到路径中,如果这个节点的左右子节点都为空,那么它就是一个叶子节点,将这条路径加入到结果数组中。这里将int转换为string使用了to_string()函数。/***Definition......
  • 基于GA遗传优化的CNN-GRU的时间序列回归预测matlab仿真
    1.算法运行效果图预览  2.算法运行软件版本MATLAB2022a 3.部分核心程序figureplot(Error2,'linewidth',2);gridonxlabel('迭代次数');ylabel('遗传算法优化过程');legend('Averagefitness');[V,I]=min(JJ);X=phen1(I,:);LR......
  • P10572 [JRKSJ R8] +1-1 题解
    样例给了我们一个很好的提示。观察样例中\(1\rightarrow4\)的路径,发现\(4\rightarrow5\)这条边走了两遍,再结合题目描述中不需要保证是简单路径的提示,我们发现:如果路径两侧分别是(\(\rightarrow\)(和)\(\rightarrow\))的话,那么中间不管怎么走都可以通过左右横跳来......
  • 微信小程序源码-公交信息在线查询系统的计算机毕业设计(附源码+演示录像+LW)
    大家好!我是职场程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • 微信小程序毕业设计-公交信息在线查询系统项目开发实战(附源码+演示视频+LW)
    大家好!我是岛上程序猿,感谢您阅读本文,欢迎一键三连哦。......
  • LibreOJ #10131. 「一本通 4.4 例 2」暗的连锁 题解 树上差分
    暗的连锁题目描述Dark是一张无向图,图中有N个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark有N−1条主要边,并且Dark的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark还有M条附加边。你的任务是把Dark斩为不连通的两部分。一开始Da......
  • [题解]P9433 [NAPC-#1] Stage5 - Conveyors
    P9433[NAPC-#1]Stage5-Conveyors题意简述给定一个\(N\)个节点的树形结构,每条边有边权,树上有\(k\)个关键点。接下来有\(q\)次询问,每次询问给定\(x,y\)两点,请计算从\(x\)开始经过这\(k\)个关键点(可以重复经过)再到\(y\)的最短路程。解题思路我们可以发现,如果\(x\)与\(y\)都......
  • js逆向-2-chrome开发者工具
    你真的认识浏览器吗?DevTool官方文档:ChromeDevTools | ChromeforDevelopers使用Chrome开发者工具调试和优化Web应用。https://developers.google.cn/web/tools/chrome-devtools/打开chrome开发者工具有一些网站会禁用右键检查和键盘事件,但是我们有很多方法可以打......
  • DAQmx数据采集---C++版本
    (一)效果展示:(二)采集流程:检索采集设备检索采集通道创建DAQ任务创建采集通道配置采集频率开始采集任务读取采集数据停止采集任务清空采集任务(三)相关接口:该接口可以检测系统已连接的相关采集卡的设备名称paramdata:分配的空间用来存储系统识别到的设备名称......