当前位置: 首页 > news >正文

QOJ #5439. Meet in the Middle 题解

Description

Byteland 有 \(n\) 座城市,从 \(1\)\(n\) 编号,且有 \(n-1\) 条双向公路和 \(n-1\) 条双向铁路连接它们。

对于任意一对城市,都可以仅通过公路互相到达;同样,仅通过铁路也能互相到达。

Alice 和 Bob 计划在 Byteland 进行 \(q\) 次长途旅行。在每次旅行中,Alice 从第 \(a\) 座城市出发,只通过公路访问一些其他城市;Bob 从第 \(b\) 座城市出发,只通过铁路访问一些其他城市。

两人最终会在同一座目的城市会合,并且在整个旅途中都不会重复访问任何城市。

你需要求出,在所有目的城市的选择中,他们路线总长度的最大值。

\(n\leq 10^5,q\leq 5\times 10^5\)

Solution

首先对于一棵树,每次求距离某个点最远的点,有两种做法,第一种是离线下来用数据结构维护距离;第二种方法是由于最远的点一定是直径端点,所以可以求出全局直径后 \(O(1)\) 做。

这里有两棵树,用某一种方法都不太好做,考虑将这两种方法结合起来。

先把询问离线下来,对第一棵树做第一种方法,即按照 dfs 的顺序枚举询问在第一棵树的点 \(x\),在移动的过程中维护 \(w_i\) 表示 \(i\)\(x\) 的距离。

现在问题等价于求 \(w_i+dis_2(i,y)\) 的最大值,这个对第二棵树的每个点 \(j\),建立一个虚点 \(j'\),与 \(j\) 连边权值为 \(w_j\) 就能将问题转化为直径问题。

但是移动 \(x\) 的过程中 \(w_i\) 会变化,考虑用线段树维护子树内节点的直径。

注意到线段树修改的过程是找到若干个线段树区间 \([l,r]\) 再区间打标记,由于整体打标记不会影响直径,所以找到这些区间后向上合并直径就行了。

时间复杂度:\(O(n\log n+q)\)

Code

#include <bits/stdc++.h>#define int int64_tusing pii = std::pair<int, int>;const int kMaxN = 1e5 + 5, kMaxQ = 5e5 + 5;int n, q, pos;
int res[kMaxQ];
std::vector<std::pair<int, int>> qq[kMaxN];struct Tree {int dfn_cnt, dfn[kMaxN], idx[kMaxN], sz[kMaxN], dis[kMaxN], st[kMaxN][18];std::vector<std::pair<int, int>> G[kMaxN];int get(int x, int y) { return dfn[x] < dfn[y] ? x : y; }void add(int u, int v, int w) { G[u].emplace_back(v, w), G[v].emplace_back(u, w); }void dfs(int u, int fa) {st[dfn[u] = ++dfn_cnt][0] = fa, idx[dfn_cnt] = u, sz[u] = 1;for (auto [v, w] : G[u]) {if (v == fa) continue;dis[v] = dis[u] + w;dfs(v, u);sz[u] += sz[v];}}int LCA(int x, int y) {if (x == y) return x;if (dfn[x] > dfn[y]) std::swap(x, y);int k = std::__lg(dfn[y] - dfn[x]);return get(st[dfn[x] + 1][k], st[dfn[y] - (1 << k) + 1][k]);}int getdis(int x, int y) { return dis[x] + dis[y] - 2 * dis[LCA(x, y)]; }void prework() {dfn_cnt = 0, dfs(1, 0);for (int i = 1; i <= std::__lg(dfn_cnt); ++i)for (int j = 1; j <= dfn_cnt - (1 << i) + 1; ++j)st[j][i] = get(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);}
} t1, t2;int calc(int x, int y) { return t1.getdis(pos, x) + t1.getdis(pos, y) + t2.getdis(x, y); }pii merge(pii a, pii b) {if (!a.first) return b;if (!b.first) return a;std::vector<int> vec = {a.first, a.second, b.first, b.second};pii ret = {0, 0};int now = -1e9;for (int i = 0; i < 4; ++i) {for (int j = i + 1; j < 4; ++j) {int val = calc(vec[i], vec[j]);if (val > now) ret = {vec[i], vec[j]}, now = val;}}return ret;
}struct SGT {pii t[kMaxN * 4];void pushup(int x) { t[x] = merge(t[x << 1], t[x << 1 | 1]); }void build(int x, int l, int r) {if (l == r) return void(t[x] = {t1.idx[l], t1.idx[l]});int mid = (l + r) >> 1;build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);pushup(x);}void update(int x, int l, int r, int ql, int qr) {if (l > qr || r < ql) return;else if (l >= ql && r <= qr) return;int mid = (l + r) >> 1;update(x << 1, l, mid, ql, qr), update(x << 1 | 1, mid + 1, r, ql, qr);pushup(x);}
} sgt;void dfs(int u, int fa) {for (auto [b, id] : qq[u]) {auto [x, y] = sgt.t[1];res[id] = std::max({res[id], t1.getdis(x, u) + t2.getdis(x, b), t1.getdis(y, u) + t2.getdis(y, b)});}for (auto [v, w] : t1.G[u]) {if (v == fa) continue;pos = v, sgt.update(1, 1, n, t1.dfn[v], t1.dfn[v] + t1.sz[v] - 1);dfs(v, u);pos = u, sgt.update(1, 1, n, t1.dfn[v], t1.dfn[v] + t1.sz[v] - 1);}
}void dickdreamer() {std::cin >> n >> q;for (int i = 1; i < n; ++i) {int u, v, w;std::cin >> u >> v >> w;t1.add(u, v, w);}for (int i = 1; i < n; ++i) {int u, v, w;std::cin >> u >> v >> w;t2.add(u, v, w);}for (int i = 1; i <= q; ++i) {int a, b;std::cin >> a >> b;qq[a].emplace_back(b, i);}t1.prework(), t2.prework();pos = 1, sgt.build(1, 1, n);dfs(1, 0);for (int i = 1; i <= q; ++i) std::cout << res[i] << '\n';
}int32_t main() {
#ifdef ORZXKRfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifstd::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);int T = 1;// std::cin >> T;while (T--) dickdreamer();// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";return 0;
}
http://www.sczhlp.com/news/11974/

相关文章:

  • MySQL数据库Docker镜像Linux
  • 没想到C#模式匹配,代码优雅的背后却这么丑陋
  • 某科学的珂朵莉树
  • World Map 世界地图
  • ELK日志
  • consul
  • kong和konga的应用
  • MARS算法理论和Python代码实现:用分段回归解决非线性时间序列预测问题
  • 数据密度一目了然~带你走近三维热力图
  • 详细理解,Java中多态特性的instanceof的使用!
  • 蓝队警报服务器:构建安全监控系统的实战模拟
  • 最小割树
  • English-reading
  • 在K8S中,有一家拥有非常分散的系统的跨国公司,期待解决整体代码库问题。你认为公司如何解决他们的问题?
  • 三行代码搞定AutoDock Vina批量分子对接
  • 开源免费!敲敲云APaaS零代码平台,做轻流/明道本地化的平替产品
  • 子产论政宽猛
  • Iframe时针对X-Frame-Options跨域问题
  • 一文读懂PDB格式 protein database bank
  • 深度学习为何有效及其局限性解析
  • postgresql数据库主从备份
  • 【自学嵌入式:stm32单片机】编码器接口测速
  • Node.js 连接 MySQL 数据库指南
  • 安卓目录结构openpilot
  • 状态码详解
  • 在K8S中,从单片到微服务的转变解决了开发方面的问题,但却增加了部署方面的问题。公司如何解决部署方面的问题?
  • MixOne在macOS上安装碰到的问题
  • cdn的原理介绍
  • 4、分支语句的简单应用
  • 8.14总结