[Codeforces 1051F] The Shortest Statement
[题目链接]
https://codeforces.com/contest/1051/problem/F
[算法]
注意边数 - 点数 <= 20 , 我们不妨首先求出图的任意一棵生成树 , 不在生成树内的边最多有21条 , 这些边不同的端点最多有42个
对这些点分别运行Dijkstra最短路
回答“u至v的最短路"时 , 有如下两种情况 :
1. 全部在生成树的边上 , 答案为生成树上两点距离
2. 经过非树边 , 这样 , 我们只需枚举所有不在生成树内的边的端点 , 用其到u的最短路径 + 到v的最短路径更新答案即可
时间复杂度 : O(NlogN)
[代码]
#include<bits/stdc++.h> using namespace std; #define MAXK 50 #define MAXLOG 20 const int MAXN = 1e5 + 10; const long long inf = 1e18; int tot , n , m , q , k; int head[MAXN],f[MAXN],depth[MAXN],u[MAXN],v[MAXN],w[MAXN]; int anc[MAXN][MAXLOG]; bool mark[MAXN]; long long ans[MAXN],sum[MAXN]; long long dist[MAXK][MAXN]; set< int > s; vector< pair<int,int> > G[MAXN]; struct edge { int to , w , nxt; } e[MAXN << 2]; struct query { int s , t , id; } a[MAXN]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline void addedge(int u,int v,int w) { tot++; e[tot] = (edge){v,w,head[u]}; head[u] = tot; } inline int get_root(int x) { if (f[x] == x) return x; else return f[x] = get_root(f[x]); } inline void dfs(int u,int fa) { for (int i = 1; i < MAXLOG; i++) { if (depth[u] < (1 << i)) break; anc[u][i] = anc[anc[u][i - 1]][i - 1]; } for (int i = head[u]; i; i = e[i].nxt) { int v = e[i].to , w = e[i].w; if (v == fa) continue; depth[v] = depth[u] + 1; anc[v][0] = u; sum[v] = sum[u] + w; dfs(v,u); } } inline int lca(int x,int y) { if (depth[x] > depth[y]) swap(x,y); for (int i = MAXLOG - 1; i >= 0; i--) { if (depth[anc[y][i]] >= depth[x]) y = anc[y][i]; } if (x == y) return x; for (int i = MAXLOG - 1; i >= 0; i--) { if (anc[x][i] != anc[y][i]) x = anc[x][i] , y = anc[y][i]; } return anc[x][0]; } inline long long dis(int x,int y) { return sum[x] + sum[y] - 2 * sum[lca(x,y)]; } inline void dijkstra(int s,int k) { static bool visited[MAXN]; priority_queue< pair<long long,int> > q; for (int i = 1; i <= n; i++) { dist[k][i] = inf; visited[i] = false; } dist[k][s] = 0; q.push(make_pair(0,s)); while (!q.empty()) { int cur = q.top().second; q.pop(); if (visited[cur]) continue; visited[cur] = true; for (unsigned i = 0; i < G[cur].size(); i++) { int v = G[cur][i].first , w = G[cur][i].second; if (dist[k][cur] + w < dist[k][v]) { dist[k][v] = dist[k][cur] + w; q.push(make_pair(-dist[k][v],v)); } } } } int main() { read(n); read(m); for (int i = 1; i <= m; i++) { read(u[i]); read(v[i]); read(w[i]); G[u[i]].push_back(make_pair(v[i],w[i])); G[v[i]].push_back(make_pair(u[i],w[i])); } for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= m; i++) { int x = get_root(u[i]) , y = get_root(v[i]); if (x != y) { addedge(u[i],v[i],w[i]); addedge(v[i],u[i],w[i]); f[x] = y; mark[i] = true; } } for (int i = 1; i <= m; i++) { if (!mark[i]) { s.insert(u[i]); s.insert(v[i]); } } dfs(1,-1); for (set< int > :: iterator it = s.begin(); it != s.end(); it++) dijkstra(*it,++k); read(q); while (q--) { int u , v; read(u); read(v); long long ans = dis(u,v); for (int i = 1; i <= k; i++) chkmin(ans,dist[i][u] + dist[i][v]); printf("%I64d\n",ans); } return 0; }