Solution to P6845 [CEOI2019] Dynamic Diameter
Statement
给你一棵有 \(n\) 个结点的树,边带权。
\(q\) 次操作,每次修改一个边权,修改完后求树的直径。
保证 \(2\le n\le 10^5\),\(1\le q\le 10^5\),\(1\le w\le 2\times 10^{13}\),\(1\le a_i,b_i\le n\),\(0\le c_i,e_j<w\),\(0\le d_j<n-1\)。
本题强制在线。
Solution
前置知识:欧拉序,线段树。
对一棵树我们定义 \(d_u\) 表示结点 \(u\) 到根节点的距离。
考虑直径的定义即
每次修改一条边的边权 \(w \gets w'\),必定影响这条边所连子树上的点的信息,效果是 \(d_u \gets d_u + w' - w\)。
因此考虑将 \(d_i\) 拍到欧拉序上维护。
由欧拉序的性质,发现 \(\mathrm{lca}(u, v)\) 至少会在 \(u, v\) 之间出现一次,且 \(\mathrm{lca}(u, v)\) 对应的 \(d\) 是 \(u, v\) 所在区间最小的。
所以设 \(s\) 为欧拉序 \(e\) 的长度,令序列 \(a_i=d_{e_i}\),答案就是
考虑怎么维护这个式子,就叫它直径 \(d\) 吧。
想象一下我们有一个区间 \(\mathbf{x} = [l, r)\),它有儿子们 \(\mathbf{ls} = [l, m)\) 和 \(\mathbf{rs} = [m, r)\)。分类一下:
- \(a_l\) 和 \(\min_{i=l}^{r} {a_i}\) 出现在 \([l, m)\),\(a_r\) 出现在 \([m, r)\)。
- \(a_l\) 出现在 \([l, m)\),\(a_r\) 和 \(\min_{i=l}^{r} {a_i}\) 出现在 \([m, r)\)。
那我们对每一个区间设两个式子
含义分别是某个区间,选了一个 \(a_i\) 后在 \(a_i\) 的右边(\(\mathrm{poly}(l)\))或左边(\(\mathrm{poly}(r)\))选择最小值,整个式子的最大值。有了这个东西我们就能够维护 \(d\)
怎么维护 \(\mathrm{poly}(l)\) 和 \(\mathrm{poly}(r)\)?
不难看出我们在左儿子选一个最大值,在右儿子选一个最小值,加上原来两个区间的值就能维护 \(\mathrm{poly}(l)\) 了,对于 \(\mathrm{poly}(r)\) 同理。
最后维护 \(\max\) 和 \(\min\),我们就能维护出一个 \(d\) 来。
修改呢?在欧拉序上做做文章,我们把当前要修改的边,所连的较深结点的子树进行一个区间修改即可。
设边权变化量为 \(k\) 的话,这里 \(\max \gets \max + \space k\),\(\min \gets \min + \space k\),\(\mathrm{poly}(l) \gets \mathrm{poly}(l) - \space k\),\(\mathrm{poly}(r) \gets \mathrm{poly}(r) - \space k\)(加上 \(k\) 减去 \(2k\)),\(d\) 没有变化(不涉及对子树内部边权的修改)。
做完了!时间复杂度 \(\mathcal{O}(q \log n)\)。
Code
代码中 dmt 代表 \(d\),lpoly 代表 \(\mathrm{poly}(l)\),rpoly 同理。
代码中大多数的下标是从零开始的。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
const int _Size = 16;
class io_r {
private:
static char buf[];
static int p;
FILE* f;
public:
inline void flush() { fread(buf, sizeof(char), 1 << _Size, f), p = 0; }
inline io_r& operator>>(char& ch) {
if (p == (1 << _Size)) flush();
ch = buf[p++];
return *this;
}
template <typename T>
inline io_r& operator>>(T& x) {
x = 0;
short w = 1;
char ch = 0;
do w = ch == '-' ? -1 : w, *this >> ch;
while (ch < '0' || ch > '9');
do x = (x << 3) + (x << 1) + ch - '0', *this >> ch;
while ('0' <= ch && ch <= '9');
x *= w;
return *this;
}
io_r() : f(stdin) {}
io_r(const char* Filename, const char* Mode = "r") : f(fopen(Filename, Mode)) {
if (!f) std::perror("File Opening Failed.");
}
io_r(FILE* f) : f(f) {}
};
class io_w {
private:
static char buf[];
static int p;
FILE* f;
public:
inline void flush() { fwrite(buf, p, 1, f), p = 0; }
inline io_w& operator<<(const char c) {
if (p == (1 << _Size)) flush();
buf[p++] = c;
return *this;
}
inline io_w& operator<<(const char* c) {
int len = strlen(c);
for (int i = 0; i < len; ++i) *this << c[i];
return *this;
}
template <typename T>
inline io_w& operator<<(T x) {
if (x < 0) *this << '-', x = -x;
static int s[50], d = 0;
do s[++d] = x % 10, x /= 10;
while (x);
do *this << (char)(s[d--] + '0');
while (d);
return *this;
}
~io_w() { flush(); }
io_w() : f(stdout) {}
io_w(const char* Filename, const char* Mode = "w") : f(fopen(Filename, Mode)) {}
io_w(FILE* f) : f(f) {}
};
char io_r::buf[1 << _Size];
int io_r::p = 0;
char io_w::buf[1 << _Size];
int io_w::p = 0;
// quick_io above
using i64 = long long;
struct Node {
public:
i64 dmt, min, max, lpoly, rpoly, tag;
Node(const i64 dmt = i64(), const i64 min = i64(), const i64 max = i64(),
const i64 lpoly = i64(), const i64 rpoly = i64(), const i64 tag = i64())
: dmt(dmt), min(min), max(max), lpoly(lpoly), rpoly(rpoly), tag(tag) {}
inline Node& operator<<(const i64 _tag) {
return *this = Node(dmt, min + _tag, max + _tag, lpoly - _tag, rpoly - _tag, tag + _tag);
}
};
inline Node operator+(const Node ls, const Node rs) {
return Node(std::max({ls.dmt, rs.dmt, ls.lpoly + rs.max, ls.max + rs.rpoly}),
std::min(ls.min, rs.min), std::max(ls.max, rs.max),
std::max({ls.lpoly, rs.lpoly, ls.max - 2ll * rs.min}),
std::max({ls.rpoly, rs.rpoly, rs.max - 2ll * ls.min}), i64());
}
class SegmentTree {
private:
std::vector<i64> a;
std::vector<Node> p;
size_t n;
inline size_t ls(const size_t x) { return (x << 1) + 1; }
inline size_t rs(const size_t x) { return (x << 1) + 2; }
inline void push_down(const size_t x) {
p[ls(x)] << p[x].tag;
p[rs(x)] << p[x].tag;
p[x].tag = i64();
}
void build(const size_t l, const size_t r, const size_t x = 0) {
if (r - l == 1) return p[x] << a[l], void();
size_t mid = ((l + r) >> 1);
build(l, mid, ls(x));
build(mid, r, rs(x));
p[x] = p[ls(x)] + p[rs(x)];
}
public:
void modify(const size_t ql, const size_t qr, const i64 k, const size_t l, const size_t r,
const size_t x) {
if (ql <= l && r <= qr) return p[x] << k, void();
push_down(x);
size_t mid = ((l + r) >> 1);
if (ql < mid) modify(ql, qr, k, l, mid, ls(x));
if (qr > mid) modify(ql, qr, k, mid, r, rs(x));
p[x] = p[ls(x)] + p[rs(x)];
}
Node query() { return p.front(); }
size_t size() { return a.size(); }
SegmentTree(std::vector<i64> a) : a(a), p(a.size() << 2), n(a.size()) { build(0, n, 0); }
};
// segment tree
class Tree {
private:
struct Edge {
int to, nxt;
i64 weight;
Edge(const int to, const int nxt, const i64 weight) : to(to), nxt(nxt), weight(weight) {}
};
struct Edge_t {
int father, son;
i64 weight;
Edge_t(const int father = int(), const int son = int(), const i64 weight = i64())
: father(father), son(son), weight(weight) {}
};
int n;
std::vector<Edge> ed;
std::vector<int> head;
std::vector<i64> dep;
public:
inline void add_edge(int u, int v, i64 w) {
ed.push_back(Edge(v, head[u], w));
head[u] = (int)ed.size() - 1;
}
std::vector<Edge_t> ed_t;
std::vector<int> euler, lbound, rbound;
void dfs_build_euler_order(int x, int f, i64 d) {
lbound[x] = euler.size();
euler.push_back(x);
dep[x] = d;
for (int i = head[x]; ~i; i = ed[i].nxt) {
int to = ed[i].to;
i64 weight = ed[i].weight;
if (to == f) continue;
Edge_t& cur = ed_t[i >> 1];
cur.father = x;
cur.son = to;
cur.weight = weight;
dfs_build_euler_order(to, x, d + weight);
euler.push_back(x);
}
rbound[x] = euler.size();
}
std::vector<i64> generate_dep_by_euler_order() {
std::vector<i64> dep_t;
for (int i : euler) {
dep_t.push_back(dep[i]);
}
dep_t.pop_back();
return dep_t;
}
Tree(int n) : n(n), head(n + 1, -1), dep(n + 1), ed_t(n - 1), lbound(n + 1), rbound(n + 1) {}
};
// euler order
io_r qin;
io_w qout;
int main() {
int n, q;
i64 w;
qin >> n >> q >> w;
Tree T(n);
for (int i = 1; i < n; ++i) {
int u, v;
i64 w;
qin >> u >> v >> w;
T.add_edge(u, v, w);
T.add_edge(v, u, w);
}
T.dfs_build_euler_order(1, 0, i64());
SegmentTree S(T.generate_dep_by_euler_order());
i64 last_ans = 0;
while (q--) {
int d;
i64 e;
qin >> d >> e;
d = (d + last_ans) % (n - 1);
e = (e + last_ans) % w;
auto& cur = T.ed_t[d];
S.modify(T.lbound[cur.son], T.rbound[cur.son], e - cur.weight, 0, S.size(), 0);
qout << (last_ans = S.query().dmt) << '\n';
cur.weight = e;
}
return 0;
}

浙公网安备 33010602011771号