标签:ext 路径 枚举 sizeof lca tin char efi n+1
题意:给出一个无向连通图,图中有\(n\)个点,\(m\)条边,**m\(-\)n$≤\(20**,给出\)q\(个询问,每一个询问包含两个整数\)u\(和\)v\(,对于每一次询问,输出\)u\(和\)v$之间的最短路
\(m-n\le20\) 说明图是很接近树的,但是这个图是树上有一些环
如果它是一棵树,维护一个前缀\(h\),\(h_v\)表示从根节点到\(v\)的距离,前缀和可以在一次\(dfs\)求出,对于树上两点距离,长度就是\(\large h_u+h_v-h_{lca(u,v)}\)
我们试着将\(m\)变大,\(m=n\)时,假设多的边连了\(x,y\),长度为\(z\),那么\(u,v\)间就会多了几条路,但是贡献的只有\(u\to x\to y\to v\)和\(u\to y\to x\to v\)两条路,长度为\(dis(u,x)+z+dis(v,y)\)和\(dis(v,x)+z+dis(v,y)\),与原来长度比较,三个求最小值
现在有\(m-n+1\)个非树边,这时,\(u,v\)间的路更多
考虑枚举这个路径经过的某一点\(i\),把这条路径拆成\(u\to i\)和\(i\to 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-n\le20\),所以\(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;
}
}
标签:ext 路径 枚举 sizeof lca tin char efi n+1
原文地址:https://www.cnblogs.com/shikeyu/p/13904304.html