cf 1051F The Shortest Statement

部署运行你感兴趣的模型镜像

一 原题

F. The Shortest Statement

time limit per test: 4 seconds

memory limit per test: 256 megabytes

input: standard input

output: standard output

You are given a weighed undirected connected graph, consisting of nn vertices and mm edges.

You should answer qq queries, the ii-th query is to find the shortest distance between vertices uiui and vivi.

Input

The first line contains two integers nn and m (1≤n,m≤105,m−n≤20)m (1≤n,m≤105,m−n≤20) — the number of vertices and edges in the graph.

Next mm lines contain the edges: the ii-th edge is a triple of integers vi,ui,di (1≤ui,vi≤n,1≤di≤109,ui≠vi)vi,ui,di (1≤ui,vi≤n,1≤di≤109,ui≠vi). This triple means that there is an edge between vertices uiui and vivi of weight didi. It is guaranteed that graph contains no self-loops and multiple edges.

The next line contains a single integer q (1≤q≤105)q (1≤q≤105) — the number of queries.

Each of the next qq lines contains two integers uiui and vi (1≤ui,vi≤n)vi (1≤ui,vi≤n) — descriptions of the queries.

Pay attention to the restriction m−n ≤ 20m−n ≤ 20.

Output

Print qq lines.

The ii-th line should contain the answer to the ii-th query — the shortest distance between vertices uiui and vivi.

Examples

input

Copy

3 3
1 2 3
2 3 1
3 1 5
3
1 2
1 3
2 3

output

Copy

3
4
1

二 分析

给你一个有1e5顶点的无向连通图,图里的边数最多比定点数多20。要处理1e5次查询,每次查询给定图上的两个点,返回这两个点的最短路长度。

如果没有边数的限制,那只能跑dijkstra或floyd。有这个限制的话,这个图就非常接近一棵树。树上两个点的简单路径是唯一的,求一下lca就可以了。但是生成树外最多还有20+1条边,把这些边的顶点记在一个集合里。如果最优解需要用到生成树之外的边,那么最短路径一定经过集合中的某个点,预处理时对这最多42个点各跑一次dijkstra。预处理lca复杂度O(nlgn),一次dijkstra复杂度O(nlgn+m)。单次查询复杂度是求lca的O(lgn)。

还真没见过一道CF题要写三个基础算法:并查集求生成树,最短路,LCA。。

 

三 代码

/*=====================================
*   
*   AUTHOR : maxkibble
*   CREATED: 2018.09.29 10:52:24
*
=====================================*/


#include <bits/stdc++.h>

using namespace std;

#define fi first
#define se second
#define pb push_back

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<long long, int> pli;

const int maxn = 1e5 + 5;
const int maxd = 20;
const int maxc = 45;
const ll inf = 1e15;

int n, m;
bool use[maxn];
int dep[maxn];
ll dis[maxn];
ll dijk_dis[maxc][maxn]; int cnt = 0;
int fa[maxd][maxn];
vector<pii> a[maxn];

struct Edge {
  int fr, to, len;
};

struct DSU {
  vector<int> f;
  set<int> st;
  
  DSU(int num) {
    for (int i = 0; i <= num; i++) f.pb(i);
  }
  
  int getF(int x) {
    return f[x] == x? x: f[x] = getF(f[x]);
  }
  
  void add(int x, int y, int i) {
    int fx = getF(x), fy = getF(y);
    if (fx == fy) {
      st.insert(x);
      st.insert(y);
    } else {
      if (fx > fy) swap(fx, fy);
      f[fy] = fx;
      use[i] = true;
    }
  }
};

void dfs(int cur, int pre, int distance) {
  dep[cur] = dep[pre] + 1;
  dis[cur] = dis[pre] + distance;
  fa[0][cur] = pre;
  for (auto nxt: a[cur]) {
    if (nxt.fi == pre) continue;
    dfs(nxt.fi, cur, nxt.se);
  }
}

int lca(int x, int y) {
  if (dep[x] < dep[y]) swap(x, y);
  int i = 0, d = dep[x] - dep[y];
  while (d) {
    if (d & 1) x = fa[i][x];
    i++;
    d >>= 1;
  }
  if (x == y) return x;
  for (int i = maxd - 1; i >= 0; i--) {
    if (fa[i][x] != fa[i][y]) {
      x = fa[i][x];
      y = fa[i][y];
    }
  }
  return fa[0][x];
}

void dijkstra(int st) {
  for (int i = 1; i <= n; i++) {
    dijk_dis[cnt][i] = inf;
  }
  dijk_dis[cnt][st] = 0;
  priority_queue<pli, vector<pli>, greater<pli>> q;
  for (auto item: a[st]) {
    q.push({item.se * 1ll, item.fi});
  }
  for (int i = 0; i < n - 1; i++) {
    // connected graph, no need to worry empty
    while (dijk_dis[cnt][q.top().se] != inf) q.pop();
    pli hd = q.top();
    dijk_dis[cnt][hd.se] = hd.fi;
    for (auto item: a[hd.se]) {
      q.push({hd.fi + item.se, item.fi});
    }
  }
  cnt++;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0); cout.tie(0);
  cin >> n >> m;
  vector<Edge> e(m);
  for (int i = 0; i < m; i++) {
    cin >> e[i].fr >> e[i].to >> e[i].len;
    a[e[i].fr].pb({e[i].to, e[i].len});
    a[e[i].to].pb({e[i].fr, e[i].len});
  }
  DSU dsu(n);
  for (int i = 0; i < m; i++) {
    dsu.add(e[i].fr, e[i].to, i);
  }
  for (auto item: dsu.st) {
    dijkstra(item);
  }
  for (int i = 1; i <= n; i++) {
    a[i].clear();
  }
  for (int i = 0; i < m; i++) {
    if (!use[i]) continue;
    a[e[i].fr].pb({e[i].to, e[i].len});
    a[e[i].to].pb({e[i].fr, e[i].len});
  }
  dfs(1, 0, 0);
  for (int i = 1; i < maxd; i++) {
    for (int j = 1; j <= n; j++) {
      fa[i][j] = fa[i - 1][fa[i - 1][j]];
    }
  }
  int q;
  cin >> q;
  while (q--) {
    int x, y;
    cin >> x >> y;
    ll ans = dis[x] + dis[y] - dis[lca(x, y)] * 2;
    for (int i = 0; i < cnt; i++) {
      ll tmp = dijk_dis[i][x] + dijk_dis[i][y];
      ans = min(ans, tmp);
    }
    cout << ans << endl;
  }
  return 0;
}

 

您可能感兴趣的与本文相关的镜像

Facefusion

Facefusion

AI应用

FaceFusion是全新一代AI换脸工具,无需安装,一键运行,可以完成去遮挡,高清化,卡通脸一键替换,并且Nvidia/AMD等显卡全平台支持

### 最短路径问题与最短简单路径问题的区别 最短路径问题通常是指在一个图中,找到从一个节点到另一个节点的最短路径。图可以是有向图或无向图,边的权重可以是正数、负数或零。最短路径问题的目标是找到一条路径,使得路径上所有边的权重之和最小。最短路径问题在实际应用中非常广泛,例如网络路由、交通导航和社交网络分析等[^1]。 最短简单路径问题则是最短路径问题的一个子集。它要求路径中不能包含任何环路,即路径必须是简单的。换句话说,最短简单路径必须是无环的路径。这是因为在某些图中,可能存在负权环路(negative-weight cycle),即一个环路的总权重为负数。如果路径中允许包含这样的环路,则可以通过多次绕行该环路无限次减少路径总权重,从而导致最短路径不存在。因此,在最短路径问题中,如果图中存在负权环路,则可能无法定义唯一的最短路径。为了避免这种情况,最短简单路径问题明确要求路径不能包含任何环路[^2]。 在大多数最短路径算法中,例如 Dijkstra 算法和 Bellman-Ford 算法,通常假设图中没有负权环路,并且寻找的是最短简单路径。Dijkstra 算法适用于所有边权为非负数的图,而 Bellman-Ford 算法可以处理存在负权边的情况,但不能处理负权环路。这些算法保证找到的路径是简单路径,即不包含任何重复节点的路径[^2]。 ### 示例代码:Dijkstra 算法实现最短简单路径搜索 ```python import heapq def dijkstra(graph, start): # 初始化距离字典,所有节点距离为无穷大 distances = {node: float('infinity') for node in graph} distances[start] = 0 # 优先队列 priority_queue = [(0, start)] while priority_queue: current_distance, current_node = heapq.heappop(priority_queue) # 如果当前节点已经处理过,跳过 if current_distance > distances[current_node]: continue # 遍历邻居节点 for neighbor, weight in graph[current_node].items(): distance = current_distance + weight # 如果找到更短路径,更新距离并加入队列 if distance < distances[neighbor]: distances[neighbor] = distance heapq.heappush(priority_queue, (distance, neighbor)) return distances ``` ### 总结 - **最短路径问题**关注的是在图中找到从一个节点到另一个节点的路径,使得路径的边权总和最小,允许包含环路(但在实际应用中通常避免负权环路)。 - **最短简单路径问题**则进一步限制路径不能包含环路,确保路径是“简单”的,这是大多数最短路径算法所求解的形式。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值