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;
}

浙公网安备 33010602011771号