我们登上的并非我们所选择的舞|

XichenOC

园龄:1个月粉丝:4关注:0

2025-01-20 12:55阅读: 11评论: 1推荐: 0

CF1253F Cheap Robot

Cheap Robot

题目翻译:

给一个带\(N\)个点的带权无向连通图,并给定\(k\)每经过一个边就要消耗边的边权\(w\),而当到达\(1 \sim k\)的节点处可以将点充满。求从\(a\)\(b\)所需要的最小点容量\(c\)。及一次性消耗的电量不能超过\(c\)

前置知识:

\(1.\)最近公共祖先\(LCA\)

\(2.\)最小生成树\(kruskal\)

\(3.\)并查集

\(4.\)最短路\(dijkstra\)

思路:

普通解法:

我们读完题肯定可以想到,用全源最短路求\(1 \sim k\)的最短路,再求每个节点所邻的最近的充电节点,求出里面耗电的最大值最小即可,但这样复杂度就到了\(n^2logn\),肯定会超

正解:

1.我们在仔细思考会发现,我们要尽量把机器人移到充电站,在在充电站之间移动一般是最优的,因此我们可以预处理出每个节点离充电站的最小距离\(dis_i\)。那如何求了。我们可以先建一个超级原点\(0\)号点,它与所有充电站,即\(1 \sim k\)有一条边权为零的边,则由这个点到任意节点的最短路就是这个节点到最近充电站的距离。原因是,任意非充电站节点到超级原点必经过充电站,而充电站到超级原点的边权为零,则该点到超级原点的最短路等于到充电站的最短路。因此只需跑一遍\(dijkstra\)求出\(dis_i\)即可。

建边

for(int i=1;i<=m;i++){
	int u,v,w;
	cin>>u>>v>>w;
	e[u].push_back({v,w});
	e[v].push_back({u,w});
}
for(int i=1;i<=k;i++){
	e[0].push_back({i,0});
}

\(dijkstra\)


int dis[N],vis[N];
vector<edge>e[N];
priority_queue<node>q;
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));
	dis[0]=0;
	q.push({0,0});
	while(!q.empty()){
		int u=q.top().u;
		q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(auto ed:e[u]){
			int v=ed.v,w=ed.w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				q.push({v,dis[v]});
			}
		}
	}
}

2.又由已知条件,\(c\),当前剩余电量\(k\),和点离充电站的最短路\(dis_i\)可得出关系

\[c \geq x \geq dis_i \]

而从充电站走到点至少消耗了\(dis_i\)则不等式可以改为

\[c-dis_i \geq x \geq dis_i \]

令下一个点为\(j\)边权为\(w_{i,j}\),同理有

\[c-dis_j \geq x-w_{i,j} \geq dis_j \]

将两个式子合并即可得到

\[dis_j \le c-dis_i-w_{i,j} \]

简单转换可知
\(c \geq dis_i+dis_j+w_{i,j}\)
则由此可知从\(i\)\(j\)的最小消耗电量为\(dis_i+dis_j+w_{i,j}\)


3.既然已经求出两点之间的最小消耗电量,而题目又是多次询问,求最大电容量及从\(a\)\(b\)的每条路的最大耗电量的最小值,因此我们可以先将任意两点之间的边权换成\(dis_i+dis_j+w_{i,j}\),在跑一遍\(kruskal\)求出最小生成树。在求任意两点路径上边权的最大值即可


建新图

for(int u=1;u<=n;u++){
	for(auto ed:e[u]){
		now.push_back({u,ed.v,ed.w+dis[u]+dis[ed.v]});
	}
}

\(kruskal\)


int fa[N];
int find(int x){
	if(fa[x]==x)return x;
	else return fa[x]=find(fa[x]);
}
void kruskal(){
	sort(now.begin(),now.end(),cmp);
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	int cnt=0;
	for(auto ed:now){
		if(find(ed.u)!=find(ed.v)){
			cnt++;
			tr[ed.u].push_back({ed.v,ed.w});
			tr[ed.v].push_back({ed.u,ed.w});
			fa[find(ed.u)]=find(ed.v);
			if(cnt==n-1)break;
		}
	}
}

4.要求树上的路径问题,我们可以简单想到求\(LCA\),于是直接用倍增求\(LCA\),只需要将求和改为求\(max\)即可


\(LCA\)

int f[N][22];
int deep[N],maxm[N][22];
void init(){
	for(int i=1;i<=20;i++){
        for(int j=1;j<=n;j++){
            f[j][i]=f[f[j][i-1]][i-1];
            maxm[j][i]=max(maxm[j][i-1],maxm[f[j][i-1]][i-1]);
        }
	}
}
void dfs(int p,int father){
    f[p][0]=father;
	for(auto ed:tr[p]){
		int v=ed.v,w=ed.w;
        if(v==father)continue;
		maxm[v][0]=w;
		deep[v]=deep[p]+1;
		dfs(v,p);
	}
}
int lca(int x,int y){
	int ans=0;
	if(deep[x]<deep[y])swap(x,y);
	for(int i=20;i>=0;i--){
		if(deep[f[x][i]]<deep[y]) continue;
		ans=max(ans,maxm[x][i]);
		x=f[x][i];
	}
	if(x==y) return ans;
	for(int i=20;i>=0;i--){
		if(f[x][i]==f[y][i])continue;
		ans=max(ans,max(maxm[x][i],maxm[y][i]));
		x=f[x][i];
		y=f[y][i];
	}
	ans=max(ans,max(maxm[x][0],maxm[y][0]));
	return ans;
}

完整流程:

\(1.\)建图,并将所有\(1 \sim k\)的点连向超级原点,边权为零
\(2.\)跑一遍以超级原点为起点的\(dijkstra\),求出\(dis_i\)
\(3.\)将任意两点间边的边权改为\(dis_i+dis_j+w_{i,j}\),建立新图
\(4.\)跑一遍新图\(kruskal\)求出最小生成树
\(5.\)在最小生成树上求\(LCA\),维护路径中边权的最大值,并输出结果

完整代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
struct edge{
	int v,w;
}; 
struct node{
	int u,dis;
	bool operator<(const node &a)const{return dis>a.dis;}
};
struct E{
	int u,v,w;
};
bool cmp(E x,E y){
	return x.w<y.w;
}
vector<E>now;
vector<edge>tr[N];
int n,m;
int dis[N],vis[N];
vector<edge>e[N];
priority_queue<node>q;
void dijkstra(){
	memset(dis,0x3f,sizeof(dis));
	dis[0]=0;
	q.push({0,0});
	while(!q.empty()){
		int u=q.top().u;
		q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(auto ed:e[u]){
			int v=ed.v,w=ed.w;
			if(dis[v]>dis[u]+w){
				dis[v]=dis[u]+w;
				q.push({v,dis[v]});
			}
		}
	}
}
int fa[N];
int find(int x){
	if(fa[x]==x)return x;
	else return fa[x]=find(fa[x]);
}
int f[N][22];
int deep[N],maxm[N][22];
void init(){
	for(int i=1;i<=20;i++){
        for(int j=1;j<=n;j++){
            f[j][i]=f[f[j][i-1]][i-1];
            maxm[j][i]=max(maxm[j][i-1],maxm[f[j][i-1]][i-1]);
        }
	}
}
void dfs(int p,int father){
    f[p][0]=father;
	for(auto ed:tr[p]){
		int v=ed.v,w=ed.w;
        if(v==father)continue;
		maxm[v][0]=w;
		deep[v]=deep[p]+1;
		dfs(v,p);
	}
}
int lca(int x,int y){
	int ans=0;
	if(deep[x]<deep[y])swap(x,y);
	for(int i=20;i>=0;i--){
		if(deep[f[x][i]]<deep[y]) continue;
		ans=max(ans,maxm[x][i]);
		x=f[x][i];
	}
	if(x==y) return ans;
	for(int i=20;i>=0;i--){
		if(f[x][i]==f[y][i])continue;
		ans=max(ans,max(maxm[x][i],maxm[y][i]));
		x=f[x][i];
		y=f[y][i];
	}
	ans=max(ans,max(maxm[x][0],maxm[y][0]));
	return ans;
}
void kruskal(){
	sort(now.begin(),now.end(),cmp);
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	int cnt=0;
	for(auto ed:now){
		if(find(ed.u)!=find(ed.v)){
			cnt++;
			tr[ed.u].push_back({ed.v,ed.w});
			tr[ed.v].push_back({ed.u,ed.w});
			fa[find(ed.u)]=find(ed.v);
			if(cnt==n-1)break;
		}
	}
}
signed main(){
	int k,q;
	cin>>n>>m>>k>>q;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		e[u].push_back({v,w});
		e[v].push_back({u,w});
	}
	for(int i=1;i<=k;i++){
		e[0].push_back({i,0});
	}
	dijkstra();
	for(int u=1;u<=n;u++){
		for(auto ed:e[u]){
			now.push_back({u,ed.v,ed.w+dis[u]+dis[ed.v]});
		}
	}
	kruskal();
	deep[1]=1;
	dfs(1,-1);
    init();
	
	while(q--){
		int x,y;
		cin>>x>>y;
		cout<<lca(x,y)<<endl;
	}
}

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18681142

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(11)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起