CF1051F
Posted shikeyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1051F相关的知识,希望对你有一定的参考价值。
题意:给出一个无向连通图,图中有(n)个点,(m)条边,**m(-)n$≤(20**,给出)q(个询问,每一个询问包含两个整数)u(和)v(,对于每一次询问,输出)u(和)v$之间的最短路
(m-nle20) 说明图是很接近树的,但是这个图是树上有一些环
如果它是一棵树,维护一个前缀(h),(h_v)表示从根节点到(v)的距离,前缀和可以在一次(dfs)求出,对于树上两点距离,长度就是(large h_u+h_v-h_{lca(u,v)})
我们试着将(m)变大,(m=n)时,假设多的边连了(x,y),长度为(z),那么(u,v)间就会多了几条路,但是贡献的只有(u o x o y o v)和(u o y o x o v)两条路,长度为(dis(u,x)+z+dis(v,y))和(dis(v,x)+z+dis(v,y)),与原来长度比较,三个求最小值
现在有(m-n+1)个非树边,这时,(u,v)间的路更多
考虑枚举这个路径经过的某一点(i),把这条路径拆成(u o i)和(i o v)两部分,那么什么时候(i)对更新(dis(u,v))有贡献呢
要跑一个最小生成树,这样就可以把(n-1)条树边搞出来
只需枚举((m-n+1))条非树边的最多(2(m-n+1))个端点,(dis(u,v))这条路,只有两种情况
1.没经过非树边,只经过树边,这种情况就是(dis(u,v))初始值
2.经过某条非树边,这条路径一定是经过这条非树边两端点
所以(dis(u,v))可以通过(dis(u,i)+dis(i,v))更新,因为是无向图,式子变成了(dis(i,u)+dis(i,v)),所以跑(i)点开始的单源最短路,又因为(m-nle20),所以(i)最多(42)个,所以最多跑(42)遍最短路即可
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
#define int long long
const int N = 2e5 + 25;
inline int read(){
int x = 0;char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
int n,m,Q;
int dep[N],fa[N][21],happen[N],vis[N];
ll dis[50][N],Tdis[N];
vector<int>p;
struct edge{ll u,v,w,f,next;}e[N<<1]; int head[N],tot;
inline void add(int u,int v,int z){
e[tot] = (edge){u,v,z,0,head[u]}; head[u] = tot++;
}
void dfs(int x, int _fa) {
vis[x] = 1; dep[x] = dep[_fa] + 1;
for(int i = head[x]; ~i; i = e[i].next) {
int to = e[i].v;
if(vis[to]) continue;
e[i].f = e[i ^ 1].f = 1;
Tdis[to] = Tdis[x] + (ll)e[i].w;
fa[to][0] = x;
dfs(to, x);
}
}
void Pre() {
for(int j = 1; j <= 20; j++)
for(int i = 1; i <= n; i++)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
void dijkstra(int x, int id) {
memset(dis[id], 0x7f, sizeof(dis[id])); dis[id][x] = 0;
memset(vis, 0, sizeof(vis));
priority_queue<pair<int,int> > q; q.push(make_pair(0, x));
while(!q.empty()) {
int p = q.top().second; q.pop();
if(vis[p]) continue;
for(int i = head[p]; ~i; i = e[i].next) {
int to = e[i].v;
if(dis[id][to] > dis[id][p] + e[i].w && (!vis[to]))
dis[id][to] = dis[id][p] + e[i].w, q.push(make_pair(-dis[id][to], to));
}
}
}
inline int lca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for(int i = 20; i >= 0; --i)
if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
if(x == y) return x;
for(int i = 20; i >= 0; --i)
if(fa[x][i] != fa[y][i])
x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
signed main(){
memset(head,-1,sizeof(head));
n = read(); m = read();
for(int i = 1; i <= m; i++) {
int x = read(), y = read(), z = read();
add(x, y, z);
add(y, x, z);
}
dfs(1, 0);
for(int i = 0; i < tot; i++)
if(!e[i].f) {
if(!happen[e[i].u]) p.push_back(e[i].u), happen[e[i].u] = 1;
if(!happen[e[i].v]) p.push_back(e[i].v), happen[e[i].v] = 1;
}
for(int i = 0; i < p.size(); i++)
dijkstra(p[i], i);
Pre();
int Q = read();
while(Q--) {
int x = read(), y = read();
ll ans = Tdis[x] + Tdis[y] - 2 * Tdis[lca(x, y)];
for(int i = 0; i < p.size(); i++)
ans = min(ans, dis[i][x] + dis[i][y]);
cout << ans << endl;
}
}
以上是关于CF1051F的主要内容,如果未能解决你的问题,请参考以下文章
CF1051F The Shortest Statement
[CF1051F]The Shortest Statement
题解Luogu CF1051F The Shortest Statement