CF1253F Cheap Robot 不错图论

传送门

最近做了好几道思维链条比较长的题目。居然难得都比较顺利。此题的实际思考还是非常顺畅的,很快接近正解。

题意

有n个点带权无向图,其中k个点是充电站。机器人有一个电池,容量没有确定,每次经过一条边时候会消耗边权电量(必须要大于等于才能经过),到充电站会立刻充满电。
有q询问,每次询问如果要从a充电站到b充电站,需要的最小电池容量。

题解

首先证明一个结论(证明最小生成树非常适合这种题):任意两点(假设a到b)在最小生成树上的路径,一定是所有路径中,最大值最小的。
反证:如果不是,则存在一条路径a到b,其中最大值小于生成树上的a到b路径。 我们用kruskal,那么这条不在(或者不完全在)生成树上的路径上的所有边,一定都会在这条树上路径的最大边之前被kruskal判断(因为比他小)那么等选到这条最大边的时候,a和b必然已经联通。


思考过程

在这个基础上,我们开始考虑怎么建一个合适的最小生成树。 比较原始的想法是, 如果我们能够得到每对充电站之间的距离, K^2条边来做最小生成树肯定是不错的,但是复杂度太大。

然后我开始想,能不能找到每个电站最近的电站(思考过程)会有用吗?我比较倾向于用dij找这个,做一个多源的dij, 但是这时候每个电站最近的都是他自己,怎么办?

kruscal是每次找最小的边,能不能每次找到两个最近的电站?dij?

能不能求出每个点(包括普通点)最近的电站,距离以及是哪个电站,可以。用dij就没问题。

但是对于一个电站,最近的还是他自己,要求次近电站吗?绝对不太好。
对于每个电站,可不可以用他旁边的点最近电站来得到呢?但万一旁边的点最近电站还是他呢?

正解

这时候我们接近正解, 考虑每个电站,所有最近电站为他的点肯定是连在一起的, 类似于这个电站为中心的一个圆,我们把这个范围(范围内的点最近电站都是他) 称作他的管辖范围。

自然想到, 我们可以考虑辖区边界,所有边界外一圈的最小值就是最近点,如果一条边两个端点的辖区不同, 则是边界,一条边 只能是一个边界,这可以作为复杂度基础。

(如果两个电站存在最短路径经过其他电站,该路径没用,读自想)
我们自然开始考虑,对于两个电站的一条最短路,总存在一个辖区交界处, 我们可以通过这条边统计到这个点对的最短路。
这里是假设其最短路,只经过起点终点两个点的辖区,如果中间有第三点或者更多的,那该方法不能统计到,不过我们自然也开始想要证明这种情况的无用性。
其实好证, 如果中间存在一个点,最近电站是其他, 那么起点连终点, 就不如起点连接中点,中点链接终点。
中间有多个点也基本同理, 或者你转换为多个3点的情况,至此大概得到了正确性, 剩下就是实现了, 用倍增求树上路径边权最大值

这个辖区,边界来统计的想法, 很巧妙有趣, 不知道是否第一次见, 不过到是顺畅的想到了。

实现

还是慢了最近的实现, 队友很快就敲出来了。

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue> 
#include <algorithm>
#define ll long long

using namespace std;

int read(){
    int num=0, flag=1; char c=getchar();
    while(!isdigit(c) && c!='-') c=getchar();
    if(c == '-') c=getchar(), flag=-1;
    while(isdigit(c)) num=num*10+c-'0', c=getchar();
    return num*flag;
}

struct Edge{
	ll w; int to, from;
	bool operator<(const Edge& x)const{
		return w > x.w;
	}
};
int n, m, k;
const int N = 1e5+100; 
const ll INF = 0x3f3f3f3f3f3f3f3f;
vector<Edge> p[N];
vector<Edge> edges;
vector<Edge> t[N];
int vis[N];
Edge dis[N];
int T;

int fa[N];
int find(int x){
	if(fa[x] == x) return x;
	return fa[x] = find(fa[x]);
}
int check(int u, int v){
	return find(u) == find(v); 
}
void merge(int u, int v){
	fa[find(u)] = find(v);
} 

bool cmp(Edge a, Edge b){
	return a.w < b.w; 
}

void kruskal(){
	for(int i=1; i<=n; i++){
		for(auto nex : p[i]){
			if(dis[i].from != dis[nex.to].from){
				edges.push_back({dis[i].w + nex.w + dis[nex.to].w, dis[i].from, dis[nex.to].from});
			}
		}
	}
	
	sort(edges.begin(), edges.end(), cmp);
	
	for(auto edge : edges){
		if(check(edge.from, edge.to)) continue;
		merge(edge.from, edge.to);
		t[edge.from].push_back({edge.w, edge.to});
		t[edge.to].push_back({edge.w, edge.from}); 
	}
	
	
}


const int M = 21;
int f[N][M]; ll g[N][M]; int dep[N];
void dfs(int x){
	for(auto nex : t[x]){
		if(nex.to == f[x][0]) continue;
		f[nex.to][0] = x;
		g[nex.to][0] = nex.w;
		dep[nex.to] = dep[x] + 1;
		dfs(nex.to);
	} 
}



void deal(){
	for(int i=0; i<=k; i++) {
		for(int j=0; j<M; j++){
			g[i][j] = INF;
		}
	}
	dep[1] = 1;
	dfs(1);
	
	for(int j=1; j<M; j++){
		for(int i=1; i<=k; i++){
			f[i][j] = f[f[i][j-1]][j-1];
			g[i][j] = max(g[i][j-1], g[f[i][j-1]][j-1]);
		}
	} 
}

ll lca(int x, int y){
	ll res = 0;
	if(dep[y] < dep[x]) swap(x, y);
	for(int i=M-1; i>=0; i--){
		if(dep[f[y][i]] >= dep[x]) res=max(res, g[y][i]), y = f[y][i];
	}
	if(x == y) return res;
	
	for(int i=M-1; i>=0; i--){
		if(f[x][i] != f[y][i]) res=max(res, max(g[x][i], g[y][i])), x=f[x][i], y=f[y][i];
	} 
	return max(res, max(g[x][0], g[y][0]));
}

void dij(){
	priority_queue<Edge> q;
	for(int i=1; i<=n; i++) dis[i].w = INF;
	for(int i=1; i<=k; i++){
		q.push({0, i, i});
	}
	while(!q.empty()){
		auto thi = q.top(); q.pop();
		if(thi.w > dis[thi.to].w) continue;
		dis[thi.to] = thi;
		for(auto nex : p[thi.to]){
			nex.w += thi.w; 
			nex.from = thi.from;
			if(nex.w < dis[nex.to].w) dis[nex.to] = nex, q.push(nex);
 		}
	}
}

int main(){
	n=read(), m=read(), k=read(), T=read();
	for(int i=1; i<=m; i++){
		int u=read(), v=read();
		int w=read();
		p[u].push_back({w, v});
		p[v].push_back({w, u});
	}
	
	for(int i=1; i<=k; i++) fa[i] = i;
	
	dij();
	
	kruskal();
	
	deal();
	
	while(T--){
		int a=read(), b=read();
		printf("%lld\n", lca(a, b)) ;
	}
	
    return 0;
}

posted @ 2025-08-05 21:52  ltdJcoder  阅读(4)  评论(0)    收藏  举报