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的一条简单路径。
\(n,m,Q \leq10^5, \ \ u,v\leq n, \ \ \ l,r\leq m\)
输出描述
对于每次询问操作,输出一个整数,表示车站的编号。如果没有满足条件的城市可以作为车站,输出-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,线段树。
考虑用线段树维护铁路的路线交路径 \((u,v)\),在 \(u\) 外侧距离 \(u\) 最远的车站距离 \(du\) ,在 \(v\) 外侧距离 \(v\) 最远的车站距离 \(dv\) 。
铁路合并时,我们需要求出路径交,运用到一个黑科技:
- 俩俩组合两条路径的端点求LCA,得到 \(4\) 个LCA 。
- 取深度最大的两个LCA。
- 若相同且深度大于某条路径的LCA,则交路径为空集,否则即为交路径的两个端点。
然后,我们需要求出新的 \(du,dv\) 。显然,新的最远车站一定从旧的两组最远车站得到,因此我们分别求出两条路径原先的最远车站到交路径端点的距离,这个可以直接从最远距离维护。其中,需要注意交路径的方向不一定和原先路径方向一致,交路径端点对应到最远车站更近的一侧。
更新非常容易,直接修改对应节点信息即可。
查询某个区间的铁路时,先求出这组铁路的交路径和最远车站距离,而最优车站一定是总距离的中点,我们分类讨论:
- 交路径为空集,输出 \(-1\) 。
- 否则,若中点出现在 \(u,v\) 上及其外侧,那么直接取 \(u,v\) 即可。注意这里中点不能总距离除 \(2\) 得到,因为会出现 \(u,v\) 同时是中点,但 \(u\) 编号更大却选了 \(u\) 的情况。当然也可以在判断前将编号小的换到 \(u\) 上。
- 否则,中点在 \((u,v)\) 内,我们需要找到这个中点,取 \(mid\) 为总距离除以 \(2\) 取下整,按总距离奇偶讨论:
- 总距离为奇数,则有唯一确定的中点,直接找 \((u,v)\) 路径上距离 \(u\) 为 \(mid - du\) 的点。
- 否则,有两个中点, \((u,v)\) 路径上距离 \(u\) 为 \(mid - du\) 的点和距离 \(u\) 为 \(mid+1 - du\) 的点,取编号最小的那个。
时间复杂度 \(O(n + m \log m + q \log m \log n)\)
空间复杂度 \(O(n)\)
代码
#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