维护一颗树。在每个时刻,有以下三个操作。
- ( u , v , w ) (u,v,w) (u,v,w): 向从 u u u至 v v v的路径上添加一个重要度为 w w w的任务。
- ( t ) (t) (t): 取消 t t t时刻添加的任务。
- ( x ) (x) (x) 查询所有未覆盖 x x x节点的任务中重要度的最大值。
这道题有诸多做法,然而我直接当做树链剖分练习题来做了。
对于这样的三个操作,我们可以考虑用树剖来维护。查询不覆盖某节点的任务难以直接用线段树实现,我们可以反其道而行之,将操作 1 1 1中的覆盖区间取补集,查询时输出 x x x点被 1 1 1操作覆盖的任务的最大值。这样的操作与原操作等价。考虑到一个节点会被多个任务覆盖或取消覆盖,可以向线段树的每个节点套一个大根堆,查询时对所有覆盖该点的线段的堆顶元素取 m a x max max。三个树套在一起,时间复杂度为 O ( N l o g 3 N ) O(Nlog^3N) O(Nlog3N),空间复杂度为 O ( N l o g 2 N ) O(Nlog^2N) O(Nlog2N)
这题还可以用kdtree,点分治,二分+树链交等各种优雅的做法秒杀,然而我一个都不会。
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct event {
int val, id;
inline bool operator < (const event &rhs) const {
return val < rhs.val;
}
}tmpe;
typedef priority_queue<event> heap;
const int N = 100010;
int n, m;
vector<int> E[N];
int size[N], dep[N], fa[N], son[N], top[N], seg[N], segCnt;
void dfs1(int u, int f) {
size[u] = 1; dep[u] = dep[fa[u] = f] + 1;
for (int i = 0; i < E[u].size(); i++) {
int &v = E[u][i];
if (v == f) continue;
dfs1(v, u);
size[u] += size[v];
if (size[v] > size[son[u]])
son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp; seg[u] = ++segCnt;
if (! son[u]) return;
dfs2(son[u], tp);
for (int i = 0; i < E[u].size(); i++) {
int &v = E[u][i];
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
inline void initTree() {
dfs1(1, 0); dfs2(1, 1);
}
bool disable[N<<1];
heap hp[N<<1];
#define g(l, r) (l + r | l != r)
#define o g(l, r)
#define ls g(l, mid)
#define rs g(mid + 1, r)
int gl, gr, gans;
#define cg(l, r) gl = l; \
gr = r
#define H hp[o]
void ins(int l, int r) {
if (gl > gr) return;
if (gl <= l && r <= gr) {
H.push(tmpe); return;
}
int mid = l + r >> 1;
if (gl <= mid) ins(l, mid);
if (mid + 1 <= gr) ins(mid + 1, r);
}
void query(int l, int r) {
while (! H.empty() && disable[H.top().id]) H.pop();
if (! H.empty()) gans = max(gans, H.top().val);
if (l == r) return;
int mid = l + r >> 1;
if (gl <= mid) query(l, mid);
else query(mid + 1, r);
}
event cf[110<<1];
inline void Insert(int u, int v) {
int ecnt = 0;
int f1 = top[u], f2 = top[v];
while (f1 != f2) {
if (dep[f1] < dep[f2]) {
swap(f1, f2); swap(u, v);
}
cf[++ecnt] = (event){seg[f1], 1}; cf[++ecnt] = (event){seg[u] + 1, -1};
u = fa[f1], f1 = top[u];
}
if (dep[u] < dep[v]) swap(u, v);
cf[++ecnt] = (event){seg[v], 1}; cf[++ecnt] = (event){seg[u] + 1, -1};
//O(logNlog(logN))差分求补集
sort(cf + 1, cf + 1 + ecnt);
int sum = 0, left = 1;
for (int i = 1; i <= ecnt; i++) {
if (sum == 0) {
cg(left, cf[i].val - 1); ins(1, n);
sum += cf[i].id;
} else {
sum += cf[i].id;
if (sum == 0) left = cf[i].val;
}
}
cg(left, n); ins(1, n);
}
inline void Query(int u) {
gans = -1;
cg(seg[u], seg[u]); query(1, n);
printf("%d\n", gans);
}
inline void work() {
for (int id = 1; id <= m; id++) {
int type; scanf("%d", &type);
if (type == 0) {
int a, b, v; scanf("%d%d%d", &a, &b, &v);
tmpe = (event){v, id};
Insert(a, b);
}
if (type == 1) {
int t; scanf("%d", &t);
disable[t] = true;
}
if (type == 2) {
int x; scanf("%d", &x);
Query(x);
}
}
}
int main() {
cin >> n >> m;
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
E[u].push_back(v); E[v].push_back(u);
}
initTree();
work();
return 0;
}