小 H 是一位优秀的越野赛车女选手。现在她准备在 A 山上进行赛车训练。
A 山上一共有 \(n\) 个广场,编号依次为 \(1\) 到 \(n\),这些广场之间通过 \(n - 1\) 条双向车道直接或间接地连接在一起。对于每条车道 \(i\),可以用四个正整数\(u_i, v_i, l_i, r_i\) 描述,表示车道连接广场 \(u_i\) 和 \(v_i\),其速度承受区间为 \([l_i, r_i]\),即汽车必须以不小于 \(l_i\) 且不大于 \(r_i\) 的速度经过车道 \(i\)。
小 H 计划进行 \(m\) 次训练,每次她需要选择 A 山上的一条简单路径,然后在这条路径上行驶。但小 H 不喜欢改变速度,所以每次训练时的车速都是固定的。现在小 H 告诉你她在 \(m\) 次训练中计划使用的车速,请帮助她对于每次训练,找到一条合法的路径(车速在所有车道的速度承受区间的交集内),使得路径上经过的车道数最大。
离线,边 \((u, v, l, r)\) 在 \(l\) 时刻加入,在 \(r + 1\) 时刻删除,变成维护联通块直径问题。
联通块直径有一个性质,即合并两个联通块时新直径的两个端点必然在旧的两条直径的端点之中。所以可以尝试用数据结构维护联通块直径的两个端点,然后进行联通块的分裂和合并。
在本题中由于树的形态是固定的,可以先建树处理出DFS序,用平衡树维护联通块。插入和删除边的时候根据 DFS 序将联通块所在平衡树分裂合并。
复杂度 \(O(N \log N)\)。
#include <bits/stdc++.h>
#ifdef LOCAL
#define dbg(args...) std::cerr << "\033[32;1m" << #args << " -> ", err(args)
#else
#define dbg(...)
#endif
inline void err() { std::cerr << "\033[0m\n"; }
template<class T, class... U>
inline void err(const T &x, const U &... a) { std::cerr << x << ‘ ‘; err(a...); }
template <class T>
inline void readInt(T &w) {
char c, p = 0;
while (!isdigit(c = getchar())) p = c == ‘-‘;
for (w = c & 15; isdigit(c = getchar());) w = w * 10 + (c & 15);
if (p) w = -w;
}
template <class T, class... U>
inline void readInt(T &w, U &... a) { readInt(w), readInt(a...); }
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }
typedef long long LL;
typedef std::pair<int, int> PII;
constexpr int N(7e4 + 5);
int n, m;
int st[18][N << 1], in[N], out[N], dep[N], cnt, lg2[N << 1], siz[N];
std::vector<int> g[N];
void dfs(int x) {
static int dfs_clock;
in[x] = ++dfs_clock;
st[0][++cnt] = x;
siz[x] = 1;
for (int y: g[x])
if (!in[y]) {
dep[y] = dep[x] + 1;
dfs(y);
siz[x] += siz[y];
st[0][++cnt] = x;
}
out[x] = cnt;
}
inline int get(int x, int y) { return dep[x] < dep[y] ? x : y; }
inline int lca(int x, int y) {
x = out[x], y = out[y];
if (x > y) std::swap(x, y);
int k = lg2[y - x + 1];
return get(st[k][y], st[k][x + (1 << k) - 1]);
}
inline int dis(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)] << 1); }
struct Data {
int x, y, d;
Data(): x(0), y(0), d(0) {}
Data(int a, int b): x(a), y(b), d(dis(a, b)) { if (x > y) std::swap(x, y); }
inline bool operator<(const Data &r) const {
return d < r.d || d == r.d && x < r.x || d == r.d && x == r.x && y < r.y;
}
inline Data operator+(const Data &r) const {
if (!x) return r;
if (!r.x) return *this;
return std::max({
*this, r, Data(x, r.x), Data(x, r.y), Data(y, r.x), Data(y, r.y)
});
}
};
struct Node { // NRTreap
Node *ls, *rs, *fa;
Data w;
int id, rnd;
} t[N], *null = t;
inline void pushup(Node *o) { o->w = Data(o - t, o - t) + o->ls->w + o->rs->w; }
void split(Node *o, int k, Node *&x, Node *&y) {
if (o == null) {
x = y = null; return;
}
if (o->id <= k)
x = o, split(o->rs, k, o->rs, y), o->rs->fa = o, y->fa = null;
else
y = o, split(o->ls, k, x, o->ls), o->ls->fa = o, x->fa = null;
pushup(o);
}
Node *merge(Node *x, Node *y) {
if (x == null) return y;
if (y == null) return x;
return x->rnd < y->rnd
? (x->rs = merge(x->rs, y), x->rs->fa = x, pushup(x), x)
: (y->ls = merge(x, y->ls), y->ls->fa = y, pushup(y), y);
}
void init() {
dfs(1);
for (int i = 2; i <= cnt; i++) lg2[i] = lg2[i >> 1] + 1;
for (int i = 1; i < 18; i++)
for (int j = 1 << i; j <= cnt; j++)
st[i][j] = get(st[i - 1][j], st[i - 1][j - (1 << i - 1)]);
null->ls = null->rs = null->fa = null;
for (int i = 1; i <= n; i++) {
t[i].ls = t[i].rs = t[i].fa = null;
t[i].w = Data(i, i);
t[i].id = in[i];
t[i].rnd = rand();
}
}
struct Edge {
int tp, t, u, v;
Edge(int a, int b, int c, int d): tp(a), t(b), u(c), v(d) {}
inline bool operator<(const Edge &rhs) const {
return t < rhs.t || t == rhs.t && tp < rhs.tp;
}
};
int main() {
freopen("racing.in", "r", stdin);
freopen("racing.out", "w", stdout);
readInt(n, m);
std::vector<Edge> e; e.reserve(n - 1 << 1);
for (int i = 1, x, y, l, r; i < n; i++) {
readInt(x, y, l, r);
e.emplace_back(0, l, x, y);
e.emplace_back(1, r + 1, x, y);
g[x].push_back(y);
g[y].push_back(x);
}
std::sort(e.begin(), e.end());
init();
std::vector<PII> q(m);
for (int i = 0; i < m; i++) readInt(q[i].first), q[i].second = i;
std::sort(q.begin(), q.end());
std::vector<int> ans(m);
std::multiset<int> diameters;
for (int i = 1; i <= n; i++) diameters.insert(0);
std::function<void(int, int)> link = [&](int x, int y) {
if (dep[x] < dep[y]) std::swap(x, y);
assert(in[x] > in[y]);
Node *a = t + x, *b = t + y, *c;
while (a->fa != null) a = a->fa;
while (b->fa != null) b = b->fa;
diameters.erase(diameters.find(a->w.d));
diameters.erase(diameters.find(b->w.d));
split(b, in[x], b, c);
b = merge(merge(b, a), c);
diameters.insert(b->w.d);
}, cut = [&](int x, int y) {
if (dep[x] < dep[y]) std::swap(x, y);
assert(in[x] > in[y]);
Node *a = t + x, *b, *c;
while (a->fa != null) a = a->fa;
diameters.erase(diameters.find(a->w.d));
split(a, in[x] - 1, a, b);
split(b, in[x] + siz[x] - 1, b, c);
a = merge(a, c);
diameters.insert(a->w.d);
diameters.insert(b->w.d);
};
auto ep = e.begin();
for (auto [t, id]: q) {
for (; ep != e.end() && ep->t <= t; ep++) {
(ep->tp ? cut : link)(ep->u, ep->v);
}
ans[id] = *diameters.rbegin();
}
for (int i: ans) printf("%d\n", i);
return 0;
}
以时间(即题中的速度)作为值域建立线段树,边 \((u, v, l, r)\) 直接挂在区间 \([l, r]\) 对应的线段树节点上,然后前序遍历线段树,进入一个节点时插入节点上挂的边,在叶节点统计答案,退出节点时可以直接回滚。
这种解法我没写(咕咕咕)。
这是一种比较通用的方法。
直接用 LCT 维护联通块直径。但是 LCT 是维护链的,想要维护连通块需要存虚子树信息。
但是直径不可减,所以直接开平衡树暴力维护所有虚子树直径。
复杂度 \(O(N \log^2N)\)。
#include <bits/stdc++.h>
#define dbg(args...) std::cerr << "\033[31;1m" << #args << " -> ", err(args)
inline void err() { std::cerr << "\033[0m\n"; }
template<class T, class... U>
inline void err(const T &x, const U &... a) { std::cerr << x << ‘ ‘; err(a...); }
using PII = std::pair<int, int>;
constexpr int N(2e5 + 5);
int n;
namespace Tree {
std::vector<int> g[N];
inline void addEdge(int x, int y) {
g[x].emplace_back(y), g[y].emplace_back(x);
}
int dep[N], in[N], out[N], st[18][N << 1], cnt, lg2[N << 1];
void dfs(int x) {
in[x] = ++cnt, st[0][cnt] = x;
for (int y: g[x]) if (!in[y]) dep[y] = dep[x] + 1, dfs(y), st[0][++cnt] = x;
out[x] = cnt;
}
inline int get(int x, int y) { return dep[x] < dep[y] ? x : y; }
inline int lca(int x, int y) {
x = out[x], y = out[y];
if (x > y) std::swap(x, y);
int k = lg2[y - x + 1];
return get(st[k][y], st[k][x + (1 << k) - 1]);
}
inline int dis(int x, int y) { return dep[x] + dep[y] - (dep[lca(x, y)] << 1); }
void init() {
dfs(1);
for (int i = 2; i <= cnt; i++) lg2[i] = lg2[i >> 1] + 1;
for (int i = 1; i < 18; i++)
for (int j = 1 << i; j <= cnt; j++)
st[i][j] = get(st[i - 1][j], st[i - 1][j - (1 << i - 1)]);
}
} // Tree
using Tree::dis;
struct Data { // Tree diameter
int x, y, d;
Data(): x(0), y(0), d(0) {}
Data(int x, int y): x(x), y(y), d(dis(x, y)) {
if (this->x > this->y) std::swap(this->x, this->y);
}
inline bool operator<(const Data &r) const {
return d < r.d || d == r.d && x < r.x || d == r.d && x == r.x && y < r.y;
}
inline Data operator+(const Data &r) const {
if (!x) return r;
if (!r.x) return *this;
return std::max({
*this, r, Data(x, r.x), Data(x, r.y), Data(y, r.x), Data(y, r.y)
});
}
};
namespace NRTreap {
struct Node {
Node *ls, *rs;
Data val, sum;
int rnd;
} t[N], *null = t, *trash[N], **tp = trash;
Node *newNode(const Data &v) {
static Node *p = t + 1;
static std::mt19937 gen(19260817);
Node *o = trash == tp ? p++ : *tp--;
o->ls = o->rs = null;
o->val = o->sum = v;
o->rnd = gen();
return o;
}
inline void pushup(Node *o) { o->sum = o->ls->sum + o->rs->sum + o->val; }
void split(Node *o, Data v, Node *&x, Node *&y) {
if (o == null) {
x = y = null; return;
}
if (v < o->val)
y = o, split(o->ls, v, x, o->ls);
else
x = o, split(o->rs, v, o->rs, y);
pushup(o);
}
Node *merge(Node *x, Node *y) {
if (x == null) return y;
if (y == null) return x;
return x->rnd < y->rnd
? (x->rs = merge(x->rs, y), pushup(x), x)
: (y->ls = merge(x, y->ls), pushup(y), y);
}
void ins(Node *&o, Data v) {
Node *a, *b, *c = newNode(v);
// dbg(v.x, v.y, v.d);
split(o, v, a, b);
o = merge(merge(a, c), b);
}
void del(Node *&o, Data v) {
Node *a, *b, *c;
split(o, v, a, b);
v.y--;
split(a, v, a, c);
assert(c != null);
*++tp = c;
o = merge(a, b);
}
void init() { null->ls = null->rs = null; }
}
namespace LinkCutTree {
using NRTreap::ins;
using NRTreap::del;
struct Node {
Node *s[2], *fa;
bool rev;
Data val, sum;
NRTreap::Node *vir;
} t[N], *null = t;
#define ls s[0]
#define rs s[1]
inline void pushup(Node *o) {
o->sum = o->val + o->ls->sum + o->rs->sum + o->vir->sum;
}
inline void rever(Node *o) { o->rev ^= 1, std::swap(o->ls, o->rs); }
inline void pushdown(Node *o) { if (o->rev) o->rev = 0, rever(o->ls), rever(o->rs); }
inline bool nrt(Node *o) { return o == o->fa->ls || o == o->fa->rs; }
inline void rot(Node *o) {
Node *f = o->fa, *ff = f->fa;
int k = o == f->rs;
if (nrt(f)) ff->s[f == ff->rs] = o;
o->fa = ff;
f->s[k] = o->s[!k], o->s[!k]->fa = f;
o->s[!k] = f, f->fa = o;
pushup(f);
}
inline void splay(Node *o) {
static Node *s[N], **top = s;
for (Node *x = o; nrt(x); x = x->fa) *++top = x->fa;
while (top != s) pushdown(*top--);
for (pushdown(o); nrt(o); rot(o)) {
Node *f = o->fa, *ff = f->fa;
if (nrt(f))
rot((o == f->rs) == (f == ff->rs) ? f : o);
}
pushup(o);
}
inline void access(Node *o) {
for (Node *x = null; o != null; x = o, o = o->fa) {
splay(o);
if (x != null) del(o->vir, x->sum);
if (o->rs != null) ins(o->vir, o->rs->sum);
o->rs = x;
pushup(o);
}
}
inline void makert(Node *o) { access(o), splay(o), rever(o); }
inline void split(Node *x, Node *y) { makert(x), access(y), splay(y); }
std::multiset<int> diameters;
inline void link(Node *x, Node *y) {
split(x, y);
// dbg(x - t, y - t, x->sum.d, y->sum.d);
diameters.erase(diameters.find(x->sum.d));
diameters.erase(diameters.find(y->sum.d));
x->fa = y;
ins(y->vir, x->sum);
pushup(y);
diameters.insert(y->sum.d);
// dbg(y->sum.d);
}
inline void cut(Node *x, Node *y) {
split(x, y);
diameters.erase(diameters.find(y->sum.d));
assert(y->ls == x);
x->fa = y->ls = null;
pushup(y);
diameters.insert(x->sum.d);
diameters.insert(y->sum.d);
}
void init() {
null->ls = null->rs = null->fa = null;
for (int i = 1; i <= n; i++) {
t[i].ls = t[i].rs = t[i].fa = null;
t[i].val = t[i].sum = Data(i, i);
t[i].vir = NRTreap::null;
diameters.insert(0);
}
}
} // LinkCutTree
using LinkCutTree::link;
using LinkCutTree::cut;
#define node(x) (LinkCutTree::t + x)
struct Edge {
int tp, t, u, v;
Edge(int a, int b, int c, int d): tp(a), t(b), u(c), v(d) {}
inline bool operator<(const Edge &rhs) const {
return t < rhs.t || t == rhs.t && tp < rhs.tp;
}
};
int main() {
freopen("racing.in", "r", stdin);
freopen("racing.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int m;
std::cin >> n >> m;
std::vector<Edge> e;
for (int i = 0, x, y, l, r; i < n - 1; i++) {
std::cin >> x >> y >> l >> r;
e.emplace_back(0, l, x, y);
e.emplace_back(1, r + 1, x, y);
Tree::addEdge(x, y);
}
Tree::init(); NRTreap::init(); LinkCutTree::init();
std::sort(e.begin(), e.end());
std::vector<PII> qry(m);
for (int i = 0; i < m; i++) std::cin >> qry[i].first, qry[i].second = i;
std::sort(qry.begin(), qry.end());
std::vector<int> ans(m);
auto ep = e.begin();
for (auto [t, id]: qry) {
for (; ep != e.end() && ep->t <= t; ep++) {
// dbg(ep->tp, ep->u, ep->v);
(ep->tp ? cut : link)(node(ep->u), node(ep->v));
}
ans[id] = *LinkCutTree::diameters.rbegin();
// dbg(id);
}
for (auto i: ans) std::cout << i << "\n";
return 0;
}
考虑直径的 DP 求法,设 \(f[x]\) 表示 \(x\) 的子树中的点与 \(x\) 的最大距离,\(g[x]\) 表示 \(x\) 的子树的直径。
将转移写成矩阵的形式:
连边就是令 \(d(x, y) = 1\),删边就是令 \(d(x, y) = -\infin\)。
本题转化为每次修改一些边的长度后求整棵树的直径,即 \(g(root)\)。
使用动态DP解决。
我不懂全局平衡二叉树,所以写的树剖+平衡树维护每条重链,复杂度 \(O(N \log^2 N)\)(但跑得飞快)。
#include <bits/stdc++.h>
#define dbg(args...) std::cerr << "\033[31;1m" << #args << " -> ", err(args)
inline void err() { std::cerr << "\033[0m\n"; }
template<class T, class... U>
inline void err(const T &x, const U &... a) { std::cerr << x << ‘ ‘; err(a...); }
constexpr int N(7e4 + 5);
using Matrix = std::array<std::array<int, 3>, 3>;
inline Matrix operator*(const Matrix &a, const Matrix &b) {
Matrix r;
r[0][0] = std::max({ a[0][0] + b[0][0], a[0][1] + b[1][0], a[0][2] + b[2][0] });
r[0][1] = std::max({ a[0][0] + b[0][1], a[0][1] + b[1][1], a[0][2] + b[2][1] });
r[0][2] = std::max({ a[0][0] + b[0][2], a[0][1] + b[1][2], a[0][2] + b[2][2] });
r[1][0] = std::max({ a[1][0] + b[0][0], a[1][1] + b[1][0], a[1][2] + b[2][0] });
r[1][1] = std::max({ a[1][0] + b[0][1], a[1][1] + b[1][1], a[1][2] + b[2][1] });
r[1][2] = std::max({ a[1][0] + b[0][2], a[1][1] + b[1][2], a[1][2] + b[2][2] });
r[2][0] = std::max({ a[2][0] + b[0][0], a[2][1] + b[1][0], a[2][2] + b[2][0] });
r[2][1] = std::max({ a[2][0] + b[0][1], a[2][1] + b[1][1], a[2][2] + b[2][1] });
r[2][2] = std::max({ a[2][0] + b[0][2], a[2][1] + b[1][2], a[2][2] + b[2][2] });
return r;
}
int n, m, fa[N], son[N], top[N];
std::vector<int> g[N];
std::multiset<int> vir_f[N], vir_g[N];
struct Node { // Binary Search Tree
Node *ls, *rs, *fa;
Matrix val, sum;
} t[N], *root;
inline void pushup(Node *o) {
o->sum = o->ls ? o->ls->sum * o->val : o->val;
if (o->rs) o->sum = o->sum * o->rs->sum;
}
inline void update(int x, int w) {
auto &v = t[x].val;
auto it = vir_f[x].rbegin();
v[0][0] = w, v[0][1] = -N, v[0][2] = *it;
v[1][0] = *it + w, v[1][1] = 0, v[1][2] = std::max(vir_f[x].size() > 1 ? *++it + *vir_f[x].rbegin() : *it, *vir_g[x].rbegin());
v[2][0] = v[2][1] = -N, v[2][2] = 0;
}
int dfs1(int x) {
int sx = 1, sm = 0;
for (int y: g[x]) {
if (y == fa[x]) continue;
fa[y] = x;
int sy = dfs1(y);
sx += sy;
if (sy > sm) sm = sy, son[x] = y;
}
vir_f[x].insert(0);
vir_g[x].insert(0);
for (int y: g[x]) {
if (y == fa[x] || y == son[x]) continue;
vir_f[x].insert(-N);
vir_g[x].insert(0);
}
if (son[x]) update(x, -N);
return sx;
}
int s[N];
Node *build(int l, int r) {
if (l > r) return nullptr;
int m = l + r >> 1;
Node *o = t + s[m];
o->ls = build(l, m - 1), o->rs = build(m + 1, r);
if (o->ls) o->ls->fa = o;
if (o->rs) o->rs->fa = o;
pushup(o);
return o;
}
void dfs2(int x, int tp) {
top[x] = tp;
if (x == tp) {
s[0] = 0;
for (int i = x; i; i = son[i]) s[++s[0]] = i;
auto top = build(1, s[0]);
if (x > 1)
top->fa = t + fa[x];
else
root = top;
}
for (int y: g[x])
if (y != son[x] && y != fa[x])
dfs2(y, y);
if (son[x]) dfs2(son[x], tp);
}
struct Edge {
int tp, t, u, v;
Edge(int tp, int t, int u, int v): tp(tp), t(t), u(u), v(v) {}
inline bool operator<(const Edge &rhs) const { return t < rhs.t; }
};
int ans[N], val[N];
void update(int x, int y, int w) {
if (fa[y] != x) std::swap(x, y);
assert(fa[y] == x);
Node *o = t + x;
if (son[x] == y) {
update(x, w);
} else {
Node *p = t + y;
while (p->fa != o) p = p->fa;
int f = p->sum[0][2], g = p->sum[1][2];
vir_f[x].erase(vir_f[x].find(f + val[y]));
vir_f[x].insert(f + w);
update(x, val[son[x]]);
}
for (val[y] = w; o->fa; o = o->fa) {
if (o->fa->ls != o && o->fa->rs != o) {
int &f = o->sum[0][2], &g = o->sum[1][2];
x = o->fa - t, y = top[o - t];
assert(fa[y] == x);
vir_f[x].erase(vir_f[x].find(f + val[y]));
vir_g[x].erase(vir_g[x].find(g));
pushup(o);
vir_f[x].insert(f + val[y]);
vir_g[x].insert(g);
update(x, val[son[x]]);
} else {
pushup(o);
}
}
pushup(o);
}
int main() {
freopen("racing.in", "r", stdin);
freopen("racing.out", "w", stdout);
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> n >> m;
std::vector<Edge> e; e.reserve(n - 1 << 1);
for (int i = 1, x, y, l, r; i < n; i++) {
std::cin >> x >> y >> l >> r;
g[x].push_back(y), g[y].push_back(x);
e.emplace_back(1, l, x, y);
e.emplace_back(-N, r + 1, x, y);
}
std::sort(e.begin(), e.end());
for (int i = 1; i <= n; i++) val[i] = -N;
dfs1(1), dfs2(1, 1);
auto ep = e.begin();
for (int t = 1; t <= n; t++) {
for (; ep != e.end() && ep->t == t; ep++)
update(ep->u, ep->v, ep->tp);
ans[t] = root->sum[1][2];
}
while (m--) {
int x;
std::cin >> x;
std::cout << ans[x] << "\n";
}
return 0;
}
原文:https://www.cnblogs.com/HolyK/p/14077349.html