Luogu 4556 雨天的尾巴
主席树+线段树合并。
首先我们想一想如果只有一个结点的话,我们弄一个权值线段树就可以随便维护了。
那么我们可以运用差分的思想,把一个询问拆成四个操作,对于一个询问$(x, y, v)$,我们在$x$的$k$处$ + 1$,在$y$的$k$处$ + 1$,在$lca(x, y)$处$ - 1$,在$fa(lca(x, y))$处$ - 1$,那么每一个点最后的权值线段树的样子就相当于把它和它的子树中的权值线段树全部合并之后得到的线段树。
动态开点就可以了。
前置技能:线段树合并。 戳这里
这样子我们往下搜一遍把每一个点和它的儿子合并,然后记录一下答案就可以了。
不会算时间复杂度QωQ。
另外,这题数据很卡,我写了内存回收 + $queue$开了$O2$才卡过。
Code:
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; const int N = 1e5 + 5; const int Lg = 19; int n, m, maxn = 0, tot = 0, head[N], ans[N]; int fa[N][Lg], dep[N], inx[N], iny[N], inv[N], val[N]; struct Edge { int to, nxt; } e[N << 1]; inline void add(int from, int to) { e[++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } struct Innum { int val, id; } in[N]; bool cmp(const Innum &x, const Innum &y) { if(x.val != y.val) return x.val < y.val; else return x.id < y.id; } inline void swap(int &x, int &y) { int t = x; x = y; y = t; } inline void chkMax(int &x, int y) { if(y > x) x = y; } inline int min(int x, int y) { return x > y ? y : x; } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void discrete() { sort(in + 1, in + 1 + m, cmp); for(int cnt = 0, i = 1; i <= m; i++) { if(in[i].val != in[i - 1].val) ++cnt; chkMax(maxn, cnt); inv[in[i].id] = cnt; val[cnt] = in[i].val; } } void dfs(int x, int fat, int depth) { dep[x] = depth, fa[x][0] = fat; for(int i = 1; i <= 18; i++) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fat) continue; dfs(y, x, depth + 1); } } inline int getLca(int x, int y) { if(dep[x] < dep[y]) swap(x, y); for(int i = 18; i >= 0; i--) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i]; if(x == y) return x; for(int i = 18; i >= 0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } namespace PSegT { struct Node { int lc, rc, sum, col; } s[N * 60]; int root[N], nodeCnt = 0; queue <int> Q; inline void push(int x) { Q.push(x); } inline int newNode() { if(Q.empty()) return ++nodeCnt; else { int res = Q.front(); Q.pop(); return res; } } #define lc(p) s[p].lc #define rc(p) s[p].rc #define sum(p) s[p].sum #define col(p) s[p].col #define mid ((l + r) >> 1) inline void up(int p) { if(!p) return; if(sum(lc(p)) < sum(rc(p))) col(p) = col(rc(p)), sum(p) = sum(rc(p)); else col(p) = col(lc(p)), sum(p) = sum(lc(p)); } void modify(int &p, int l, int r, int x, int v) { if(!p) p = newNode(); if(l == r) { sum(p) += v; if(sum(p) > 0) col(p) = l; else col(p) = 0; return; } if(x <= mid) modify(lc(p), l, mid, x, v); else modify(rc(p), mid + 1, r, x, v); up(p); } int merge(int u, int v, int l, int r) { if(!u || !v) return u + v; int p = newNode(); if(l == r) { sum(p) = sum(u) + sum(v); if(sum(p) > 0) col(p) = l; else col(p) = 0; } else { lc(p) = merge(lc(u), lc(v), l, mid); rc(p) = merge(rc(u), rc(v), mid + 1, r); up(p); } push(u), push(v); return p; } } using namespace PSegT; void solve(int x) { for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(y == fa[x][0]) continue; solve(y); root[x] = merge(root[x], root[y], 1, maxn); } /* printf("%d: ", x); for(int i = 1; i <= maxn; i++) printf("%d ", query(root[id[x] - 1], root[id[x]], 1, maxn, i)); printf("\n"); */ ans[x] = val[s[root[x]].col]; } int main() { read(n), read(m); for(int x, y, i = 1; i < n; i++) { read(x), read(y); add(x, y), add(y, x); } dfs(1, 0, 1); // maxn = 1e5; for(int i = 1; i <= m; i++) { read(inx[i]), read(iny[i]), read(inv[i]); in[i].id = i, in[i].val = inv[i]; } discrete(); for(int x, y, v, z, w, i = 1; i <= m; i++) { x = inx[i], y = iny[i], v = inv[i]; z = getLca(x, y), w = fa[z][0]; /* vec[x].push_back(pin(v, 1)); vec[y].push_back(pin(v, 1)); vec[z].push_back(pin(v, -1)); if(w) vec[w].push_back(pin(v, -1)); */ modify(root[x], 1, maxn, v, 1); modify(root[y], 1, maxn, v, 1); modify(root[z], 1, maxn, v, -1); if(w) modify(root[w], 1, maxn, v, -1); } /* printf("\n"); for(int i = 1; i <= n; i++) { printf("%d: ", i); for(int j = 1; j <= maxn; j++) printf("%d ", query(root[id[i] - 1], root[id[i] + siz[i] - 1], 1, maxn, j)); printf("\n"); } printf("\n"); */ solve(1); for(int i = 1; i <= n; i++) printf("%d\n", ans[i]); return 0; }