NC22544 车站
题目
题目描述
一个国家有n个城市,有n-1条道路连接,保证联通。还有m条铁路,从1~m编号,第i条铁路是从ui到vi的简单路径,多次询问一段区间的铁路的车站。
一个点可以作为区间[L,R]铁路的车站满足以下条件:
1、R-L+1条铁路都经过这个车站。
2、R-L+1条铁路经过的所有城市中,离车站最远的城市,与它的距离最小。如果有多个,那么选择编号较小的。
并且存在铁路发生改变的情况。
输入描述
第一行两个整数n,m,表示城市的个数和铁路条数。
接下来n-1行,每行两个整数,描述一条道路。
接下来m行,每行两个整数,描述一条铁路(注意道路与铁路的区分)。
然后一行一个整数Q,表示询问个数。
接下来Q行,每行3个或者4个整数,描述一次操作,具体如下:
操作1:1,l,r,表示询问[l,r]铁路的车站。
操作2:2,x,u,v,表示第x条铁路变成从u到v的一条简单路径。
输出描述
对于每次询问操作,输出一个整数,表示车站的编号。如果没有满足条件的城市可以作为车站,输出-1。
示例1
输入
6 3 1 2 1 3 2 4 2 5 4 6 4 5 2 3 1 3 7 1 1 1 1 1 2 1 1 3 2 3 1 6 1 2 3 2 1 1 4 1 1 2
输出
2 2 -1 2 1
题解
知识点:树链剖分,LCA,线段树。
考虑用线段树维护铁路的路线交路径 ,在 外侧距离 最远的车站距离 ,在 外侧距离 最远的车站距离 。
铁路合并时,我们需要求出路径交,运用到一个黑科技:
- 俩俩组合两条路径的端点求LCA,得到 个LCA 。
- 取深度最大的两个LCA。
- 若相同且深度大于某条路径的LCA,则交路径为空集,否则即为交路径的两个端点。
然后,我们需要求出新的 。显然,新的最远车站一定从旧的两组最远车站得到,因此我们分别求出两条路径原先的最远车站到交路径端点的距离,这个可以直接从最远距离维护。其中,需要注意交路径的方向不一定和原先路径方向一致,交路径端点对应到最远车站更近的一侧。
更新非常容易,直接修改对应节点信息即可。
查询某个区间的铁路时,先求出这组铁路的交路径和最远车站距离,而最优车站一定是总距离的中点,我们分类讨论:
- 交路径为空集,输出 。
- 否则,若中点出现在 上及其外侧,那么直接取 即可。注意这里中点不能总距离除 得到,因为会出现 同时是中点,但 编号更大却选了 的情况。当然也可以在判断前将编号小的换到 上。
- 否则,中点在 内,我们需要找到这个中点,取 为总距离除以 取下整,按总距离奇偶讨论:
- 总距离为奇数,则有唯一确定的中点,直接找 路径上距离 为 的点。
- 否则,有两个中点, 路径上距离 为 的点和距离 为 的点,取编号最小的那个。
时间复杂度
空间复杂度
代码
#include <bits/stdc++.h> using namespace std; using ll = long long; struct HLD { vector<int> siz, dep, fat, son, top, dfn, L, R; HLD() {} HLD(int rt, const vector<vector<int>> &g) { init(rt, g); } void init(int rt, const vector<vector<int>> &g) { assert(g.size() >= 2); int n = g.size() - 1; siz.assign(n + 1, 0); dep.assign(n + 1, 0); fat.assign(n + 1, 0); son.assign(n + 1, 0); top.assign(n + 1, 0); dfn.assign(n + 1, 0); L.assign(n + 1, 0); R.assign(n + 1, 0); function<void(int, int)> dfsA = [&](int u, int fa) { siz[u] = 1; dep[u] = dep[fa] + 1; fat[u] = fa; for (auto v : g[u]) { if (v == fa) continue; dfsA(v, u); siz[u] += siz[v]; if (siz[v] > siz[son[u]]) son[u] = v; } }; dfsA(rt, 0); int dfncnt = 0; function<void(int, int)> dfsB = [&](int u, int tp) { top[u] = tp; dfn[++dfncnt] = u; L[u] = dfncnt; if (son[u]) dfsB(son[u], tp); for (auto v : g[u]) { if (v == fat[u] || v == son[u]) continue; dfsB(v, v); } R[u] = dfncnt; }; dfsB(rt, rt); } int LCA(int u, int v) { while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) swap(u, v); u = fat[top[u]]; } return dep[u] < dep[v] ? u : v; } int dist(int u, int v) { return dep[u] + dep[v] - 2 * dep[LCA(u, v)]; } int jump(int u, int k) { if (dep[u] < k) return -1; int d = dep[u] - k; while (dep[top[u]] > d) u = fat[top[u]]; return dfn[L[u] - dep[u] + d]; } bool isAncester(int u, int v) { return L[u] <= L[v] && L[v] <= R[u]; } pair<int, int> path_intersection(pair<int, int> A, pair<int, int> B) { auto [u1, v1] = A; auto [u2, v2] = B; vector<int> lca = { LCA(u1,u2),LCA(u1,v2),LCA(v1,u2),LCA(v1,v2) }; sort(lca.begin(), lca.end(), [&](int a, int b) {return dep[a] > dep[b];}); int p1 = lca[0], p2 = lca[1]; if (p1 == p2 && (dep[p1] < dep[LCA(u1, v1)] || dep[p1] < dep[LCA(u2, v2)])) return { -1,-1 }; else return { p1,p2 }; } }; template<class T, class F> class SegmentTree { int n; vector<T> node; void update(int rt, int l, int r, int x, F f) { if (r < x || x < l) return; if (l == r) return node[rt] = f(node[rt]), void(); int mid = l + r >> 1; update(rt << 1, l, mid, x, f); update(rt << 1 | 1, mid + 1, r, x, f); node[rt] = node[rt << 1] + node[rt << 1 | 1]; } T query(int rt, int l, int r, int x, int y) { if (r < x || y < l) return T(); if (x <= l && r <= y) return node[rt]; int mid = l + r >> 1; return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y); } public: SegmentTree(int _n = 0) { init(_n); } SegmentTree(const vector<T> &src) { init(src); } void init(int _n) { n = _n; node.assign(n << 2, T()); } void init(const vector<T> &src) { assert(src.size() >= 2); init(src.size() - 1); function<void(int, int, int)> build = [&](int rt, int l, int r) { if (l == r) return node[rt] = src[l], void(); int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); node[rt] = node[rt << 1] + node[rt << 1 | 1]; }; build(1, 1, n); } void update(int x, F f) { update(1, 1, n, x, f); } T query(int x, int y) { return query(1, 1, n, x, y); } }; const int N = 1e5 + 7; vector<int> g[N]; HLD hld; struct T { int u = -1, v = -1; int du = -1, dv = -1; friend T operator+(const T &a, const T &b) { if (a.u == -1) return b; if (b.u == -1) return a; if (a.u == 0 || b.u == 0) return { 0,0,0,0 }; auto [p1, p2] = hld.path_intersection({ a.u, a.v }, { b.u,b.v }); if (p1 == -1) return { 0,0,0,0 }; T ans = T(); ans.u = p1; ans.v = p2; vector<int> dis; dis = { hld.dist(a.u, ans.u),hld.dist(a.u, ans.v),hld.dist(a.v, ans.u),hld.dist(a.v, ans.v) }; if (dis[0] < dis[1]) { ans.du = max(ans.du, a.du + dis[0]); ans.dv = max(ans.dv, a.dv + dis[3]); } else { ans.du = max(ans.du, a.dv + dis[2]); ans.dv = max(ans.dv, a.du + dis[1]); } dis = { hld.dist(b.u, ans.u),hld.dist(b.u, ans.v),hld.dist(b.v, ans.u),hld.dist(b.v, ans.v) }; if (dis[0] < dis[1]) { ans.du = max(ans.du, b.du + dis[0]); ans.dv = max(ans.dv, b.dv + dis[3]); } else { ans.du = max(ans.du, b.dv + dis[2]); ans.dv = max(ans.dv, b.du + dis[1]); } return ans; } }; struct F { int u, v; T operator()(const T &x) { return { u,v,0,0 }; } }; SegmentTree<T, F> sgt; int find(int u, int v, int k) { int lca = hld.LCA(u, v); if (hld.dist(u, lca) < k) return hld.jump(v, hld.dist(u, v) - k); else return hld.jump(u, k); } int query(int l, int r) { auto [u, v, du, dv] = sgt.query(l, r); if (u == 0) return -1; int d = hld.dist(u, v); //! 下面不能用mid取下整判断,否则会出现两个(u,v)内中点,但只能选标号最小的那个,就会判断错误 if (du >= d + dv) return u; // 中点(如果有两个,则会取到右侧中点)落在u及其左侧 else if (dv >= d + du) return v; // 中点(如果有两个,则会取到左侧中点)落在v及其右侧 int mid = du + d + dv >> 1; if ((du + d + dv) & 1) return min(find(u, v, mid - du), find(u, v, mid + 1 - du)); else return find(u, v, mid - du); } int main() { std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin >> n >> m; for (int i = 1;i <= n - 1;i++) { int u, v; cin >> u >> v; g[u].push_back(v); g[v].push_back(u); } hld.init(1, vector<vector<int>>(g, g + n + 1)); vector<T> src(m + 1); for (int i = 1;i <= m;i++) { int u, v; cin >> u >> v; src[i] = { u,v,0,0 }; } sgt.init(src); int q; cin >> q; while (q--) { int op; cin >> op; if (op == 1) { int l, r; cin >> l >> r; cout << query(l, r) << '\n'; } else { int x, u, v; cin >> x >> u >> v; sgt.update(x, { u,v }); } } return 0; }
本文来自博客园,作者:空白菌,转载请注明原文链接:https://www.cnblogs.com/BlankYang/p/17499293.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
2022-06-23 NC50038 kotori和糖果
2022-06-23 Contest
2022-06-23 NC19115 选择颜色
2022-06-23 NC23046 华华教月月做数学
2022-06-23 NC14731 逆序对
2022-06-23 NC15052 求最值
2022-06-23 NC50999 表达式计算4