Codeforces 1163F. Indecisive Taxi Fee

Codeforces 1163F. Indecisive Taxi Fee

通过大力分类讨论, 容易把原题归约为“删除一条边 \(j\)​ 之后求 \(1\rightarrow n\)​ 的最短路”.

注意到删去非原最短路径上的边对答案无影响. 只需考虑当删去的边在原最短路径上这种情况.

\(d_1(x),\,d_n(x)\) 分别为点 \(1,\,n\) 到点 \(x\) 的最短路, \(\mathrm E\)\(1\rightarrow n\) 最短路径上边的集合.

结论 在删除最短路径上的边 \(j\) 之后设 \(1\rightarrow n\) 的最短路为 \(W\) , 则一定存在非原最短路径上的边 \(i\) 满足经过边 \(i\) 的最短路径长为 \(W\), 即 \(\exist \,e_i(u,v,w)\notin \mathrm E\ \ \mathrm {s.t.}\ \ d_1(u)+d_n(v)+w=W\)

证明 咕咕咕 (其实是我水平太菜不会)

于是, 我们就得到了一个暴力的方法: 枚举每条边 \(i\notin E\) , 计算经过边 \(i\) 的最短路径长. 总时间复杂度为 \(\mathcal O(n^2)\) . 考虑优化.

设最短路径为 \(a_0,a_1,a_2,\ldots,a_k\) , 其中 \(a_0=1,\,a_k=n\) . 考虑枚举边 \(e(u,\,v,\,w)\notin E\) . 容易发现 \(1\rightarrow u\) 的最短路径为 \(1\rightarrow a_i\Rightarrow x\rightarrow u\ \ (0\le i\le k)\) ; 同理, \(v\rightarrow n\) 的最短路径为 \(v\rightarrow y\Rightarrow a_j\rightarrow n\ \ (0\le j\le k)\) . 其中点 \(x,y\) 均不在原最短路径上. 因此当且仅当删去直径上的边为 \((a_i,a_{i+1}),\,(a_{i+1},a_{i+2}),\,\ldots(a_{j-1},a_j)\) 时, 边 \(e\) 对答案有影响, 且影响为 \(d_1(u)+d_n(v)+w\). 换句话说, 一条边 \(e\) 的影响为直径上边的一个区间. 于是可以把问题看作静态区间取min.

由于是静态的, 可以离线, 按照影响从大到小排序, 然后变成区间赋值问题. 使用线段树实现, 时间复杂度为 \(\mathcal O(n\log n)\) . 当然也可以不离线, 直接倍增后差分, 时间复杂度不变.

参考代码
#include <bits/stdc++.h>
using namespace std;
template<typename _Tp> _Tp &min_eq(_Tp &x, const _Tp &y) { return x = min(x, y); }
template<typename _Tp> _Tp &max_eq(_Tp &x, const _Tp &y) { return x = max(x, y); }
static constexpr int64_t inf = 0x3f3f3f3f3f3f3f3f;
static constexpr int Maxn = 2e5 + 5, LOG = 19;
int n, m, q;
int eu[Maxn], ev[Maxn];
int64_t ew[Maxn];
vector<pair<int, int>> g[Maxn];
vector<int> T[Maxn];
int64_t d1[Maxn], dn[Maxn];
int par[LOG][Maxn], par_n[Maxn];
int pe1[Maxn], pen[Maxn];
bool ondiam[Maxn];
void dijkstra(int st, int64_t *d, int *f, int *fe) {
  fill(d + 1, d + n + 1, inf);
  fill(f + 1, f + n + 1, 0);
  priority_queue<pair<int64_t, int>> pq;
  pq.push({-(d[st] = 0), st}), f[st] = 0, fe[st] = 0;
  while (!pq.empty()) {
    int u = pq.top().second;
    int64_t di = -pq.top().first;
    pq.pop();
    if (di != d[u]) continue;
    for (const auto &[v, i]: g[u])
      if (d[v] > d[u] + ew[i])
        pq.push({-(d[v] = d[u] + ew[i]), v}), f[v] = u, fe[v] = i;
  }
} // dijkstra
int dep[Maxn];
void lca_init(int u, int depth) {
  dep[u] = depth;
  for (int j = 1; j < LOG; ++j)
    par[j][u] = par[j - 1][par[j - 1][u]];
  for (const int &v: T[u])
    lca_init(v, depth + 1);
} // lca_init
int get_lca(int u, int v) {
  if (dep[u] > dep[v]) swap(u, v);
  for (int i = LOG - 1; i >= 0; --i)
    if ((dep[v] - dep[u]) >> i & 1)
      v = par[i][v];
  if (u == v) return u;
  for (int i = LOG - 1; i >= 0; --i)
    if (par[i][u] != par[i][v])
      u = par[i][u], v = par[i][v];
  return par[0][u];
} // get_lca
int64_t mn[LOG][Maxn];
void modify_chain(int u, int v, int64_t w) {
  if (dep[u] > dep[v]) swap(u, v);
  for (int i = LOG - 1; i >= 0; --i)
    if ((dep[v] - dep[u]) >> i & 1)
      min_eq(mn[i][v], w), v = par[i][v];
} // modify_chain
int main(void) {
  scanf("%d%d%d", &n, &m, &q);
  for (int i = 1; i <= m; ++i) {
    scanf("%d%d%lld", &eu[i], &ev[i], &ew[i]);
    g[eu[i]].push_back({ev[i], i});
    g[ev[i]].push_back({eu[i], i});
  }
  dijkstra(1, d1, par[0], pe1), dijkstra(n, dn, par_n, pen);
  for (int i = 1; i <= n; ++i)
    if (par[0][i] != 0) T[par[0][i]].push_back(i);
  lca_init(1, 1);
  fill(ondiam + 1, ondiam + n + 1, false);
  for (int i = n; i != 1; i = par[0][i]) ondiam[pe1[i]] = true;
  memset(mn, inf, sizeof(mn));
  for (int i = 1; i <= m; ++i) if (!ondiam[i]) {
    int u = eu[i], v = ev[i], lu = get_lca(u, n), lv = get_lca(v, n);
    int64_t w = min(d1[u] + dn[v], d1[v] + dn[u]) + ew[i];
    modify_chain(lu, lv, w);
  }
  for (int j = LOG - 1; j >= 1; --j)
    for (int i = 1; i <= n; ++i) {
      min_eq(mn[j - 1][i], mn[j][i]);
      min_eq(mn[j - 1][par[j - 1][i]], mn[j][i]);
    }
  while (q--) {
    int i; int64_t w;
    scanf("%d%lld", &i, &w);
    if (!ondiam[i]) {
      int64_t ans = w + min(d1[eu[i]] + dn[ev[i]], d1[ev[i]] + dn[eu[i]]);
      printf("%lld\n", min_eq(ans, d1[n]));
    } else {
      int u = eu[i], v = ev[i];
      if (dep[u] > dep[v]) swap(u, v);
      int64_t ans = mn[0][v];
      printf("%lld\n", min_eq(ans, d1[n] - ew[i] + w));
    }
  }
  exit(EXIT_SUCCESS);
} // main
posted @ 2021-11-13 16:36  cutx64  阅读(135)  评论(0)    收藏  举报