CF1192B Dynamic Diameter
算法
- 欧拉序
- 线段树
思路
对于这道题考虑使用欧拉序的性质:
- 对于树上的每一个子树,在欧拉序上都有一个区间与之对应。
- 对于 的最近公共祖先,在欧拉序上表现为区间 内深度最浅的节点。
所以可以将原树转化为欧拉序,将树上问题转化为序列上的区间问题,就可以使用线段树解决。
由于题目要求树的直径,所以用线段树查找区间最值,找到点 的最近公共祖先 ,则 可以表示为:
而直径就是 。
所以原题目的问题就转化为:在欧拉序上找一个三元组 ,满足如下条件:
- 最大。
- 为区间 内深度最小的节点。
而第二个条件可以由第一个条件限制,所以可以忽略。
由上面两个限制条件我们可以得到线段树需要维护的数据:
- :区间最大值。
- :区间最小值。
- (以下简称 ):三元组 中 的最大值。
- (以下简称 ):三元组 中 的最大值。
- (以下简称 ):三元组 中 的最大值。
接下来考虑如何转移 (即为 push_up(int k) 函数)。 对于 和 来说转移很简单,即为:
而对于 而言,可以从三个方向转移:
对于 同理由:
最后合并成 :
代码
#include <bits/stdc++.h>#define OnlineJudge
constexpr int N = 1e5 + 5;std::vector<int> euler_seq{-1};int in[N], out[N], num = 0;int head[N], cnt = 0;int n, q, u, v, fa[N];long long W, d, w, last = 0ll, dist[N];std::vector<std::tuple<int, int, long long>> input_edges;
class Edge { public: int to, next; long long w;
} edges[N << 4];
void add_edge(int u, int v, long long w) { edges[cnt].to = v; edges[cnt].w = w; edges[cnt].next = head[u]; head[u] = cnt++;}
class SegmentTree { private: class Values { public: // ! 对 lazy 要初始化(虽然在 build 里面写了,但不知道为什么有些还是没有初始化) long long max = 0, min = 0, left_mid = 0, mid_right = 0, left_mid_right = 0, lazy = 0; };
class NodeInfo { public: int left = 0, right = 0; Values val; };
public: NodeInfo tr[N << 2];
Values merge_tags(Values a, Values b) { Values retval;
retval.max = std::max(a.max, b.max); retval.min = std::min(a.min, b.min); retval.left_mid = std::max({a.left_mid, b.left_mid, a.max + -2 * b.min}); retval.mid_right = std::max({a.mid_right, b.mid_right, -2 * a.min + b.max}); retval.left_mid_right = std::max({a.left_mid_right, b.left_mid_right, a.left_mid + b.max, a.max + b.mid_right});
return retval; }
void modify_tags(Values& root, long long value) { root.lazy += value; root.max += value; root.min += value; root.left_mid -= value; root.mid_right -= value; }
void push_up(int k) { Values lc = tr[k << 1].val, rc = tr[k << 1 | 1].val;
int temp = tr[k].val.lazy; tr[k].val = merge_tags(lc, rc); tr[k].val.lazy = temp; }
void push_down(int k) { if (tr[k].val.lazy == 0) { return void(); }
modify_tags(tr[k << 1].val, tr[k].val.lazy); modify_tags(tr[k << 1 | 1].val, tr[k].val.lazy);
tr[k].val.lazy = 0; }
void build_tree(int k, int l, int r) { tr[k].left = l; tr[k].right = r; tr[k].val.lazy = 0;
if (tr[k].left == tr[k].right) { tr[k].val.max = tr[k].val.min = dist[euler_seq[l]]; tr[k].val.left_mid = tr[k].val.mid_right = -dist[euler_seq[l]]; tr[k].val.left_mid_right = 0;
#ifndef OnlineJudge std::cout << "BUILD:\n"; std::cout << k << " " << tr[k].left << " " << tr[k].right << " "; std::cout << tr[k].val.max << " " << tr[k].val.min << " " << tr[k].val.left_mid << " "; std::cout << tr[k].val.mid_right << ' ' << tr[k].val.left_mid_right << " " << tr[k].val.lazy << '\n'; #endif
return void(); }
int mid = (tr[k].left + tr[k].right) >> 1; int lc = k << 1, rc = k << 1 | 1;
build_tree(lc, l, mid); build_tree(rc, mid + 1, r);
push_up(k);
#ifndef OnlineJudge std::cout << "BUILD:\n"; std::cout << k << " " << tr[k].left << " " << tr[k].right << " "; std::cout << tr[k].val.max << " " << tr[k].val.min << " " << tr[k].val.left_mid << " "; std::cout << tr[k].val.mid_right << ' ' << tr[k].val.left_mid_right << " " << tr[k].val.lazy << '\n'; #endif }
void modify(int k, int l, int r, long long value) { #ifndef OnlineJudge std::cout << "MODIFY:\n"; std::cout << k << " " << tr[k].left << " " << tr[k].right << " "; std::cout << tr[k].val.max << " " << tr[k].val.min << " " << tr[k].val.left_mid << " "; std::cout << tr[k].val.mid_right << ' ' << tr[k].val.left_mid_right << " " << tr[k].val.lazy << '\n'; #endif
if (tr[k].left >= l && tr[k].right <= r) { modify_tags(tr[k].val, value); return void(); }
push_down(k);
int mid = (tr[k].left + tr[k].right) >> 1; int lc = k << 1, rc = k << 1 | 1;
if (r <= mid) { modify(lc, l, r, value); } else if (l > mid) { modify(rc, l, r, value); } else { modify(lc, l, mid, value); modify(rc, mid + 1, r, value); }
push_up(k); }
Values query(int k, int l, int r) { #ifndef OnlineJudge std::cout << "QUERY:\n"; std::cout << k << " " << tr[k].left << " " << tr[k].right << " "; std::cout << tr[k].val.max << " " << tr[k].val.min << " " << tr[k].val.left_mid << " "; std::cout << tr[k].val.mid_right << ' ' << tr[k].val.left_mid_right << " " << tr[k].val.lazy << '\n'; #endif
if (tr[k].left >= l && tr[k].right <= r) { return tr[k].val; }
push_down(k);
int mid = (tr[k].left + tr[k].right) >> 1; int lc = k << 1, rc = k << 1 | 1;
if (r <= mid) { return query(lc, l, r); } else if (l > mid) { return query(rc, l, r); } else { return merge_tags(query(lc, l, mid), query(rc, mid + 1, r)); } }} data;
void dfs(int u, int father) { euler_seq.emplace_back(u); in[u] = euler_seq.size() - 1;
for (int i = head[u]; ~i; i = edges[i].next) { int v = edges[i].to; if (v == father) continue;
fa[v] = u; dist[v] = dist[u] + edges[i].w; dfs(v, u); euler_seq.emplace_back(u); }
out[u] = euler_seq.size() - 1;}
int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr);
std::memset(head, -1, sizeof(head));
std::cin >> n >> q >> W;
for (int i = 1; i < n; i ++) { std::cin >> u >> v >> w;
add_edge(u, v, w); add_edge(v, u, w); input_edges.emplace_back(u, v, w); }
dfs(1, 1);
// ! 注意调换顺序,保证前面一个点是后面一个点的父亲 for (int i = 0; i < n - 1; i ++) { if (fa[std::get<0>(input_edges[i])] == std::get<1>(input_edges[i])) { std::swap(std::get<0>(input_edges[i]), std::get<1>(input_edges[i])); } }
data.build_tree(1, 1, euler_seq.size() - 1);
#ifndef OnlineJudge std::cout << data.query(1, 1, euler_seq.size() - 1).left_mid_right << '\n'; for (auto&& [u, v, w] : input_edges) { std::cout << u << ' ' << v << ' ' << w << '\n'; } #endif
last = 0; for (int i = 1; i <= q; i ++) { std::cin >> d >> w; d = (d + last) % (n - 1); w = (w + last) % W;
// ! 对于边 (u, v, w),满足 fa[v] == u,则修改后的 w' 只对 v 的子树有影响,所以只更新 v 的子树即可。 data.modify(1, in[std::get<1>(input_edges[d])], out[std::get<1>(input_edges[d])], w - std::get<2>(input_edges[d])); // ! 记得将边的信息更新 std::get<2>(input_edges[d]) = w; last = data.query(1, 1, euler_seq.size() - 1).left_mid_right;
std::cout << last << '\n'; }
return 0;}
Comments
Quiet notes for this article.