Link
线段树合并的板子题,等刷过几道再去补树剖做法。
注意: 线段树空间开很多倍的同时,不要忘记把root数组也同样开到很多倍
思路
其实就是树上差分+LCA,但空间复杂度
O
(
N
M
)
O(NM)
O(NM),通过线段树合并将空间复杂度降至
O
(
(
N
+
M
)
log
(
N
+
M
)
)
O((N+M)\log(N+M))
O((N+M)log(N+M))
具体来说,对树上每个点建立一棵动态开点的权值线段树,支持修改某个位置,维护区间最大值和最大值所在的位置。
代码
const int maxn = 4e5 + 10;
const int maxm = 4e5 + 10;
struct SegmentTree {
int lc, rc;
int dat;
#define lc(p) tree[p].lc
#define rc(p) tree[p].rc
#define dat(p) tree[p].dat
} tree[maxn << 5];
int rt[maxn << 5];
int tot;
int x[maxn], y[maxn], z[maxn];
void push(int p) {
if(lc(p) == 0) {
dat(p) = dat(rc(p));
rt[p] = rt[rc(p)];
return;
}
if(rc(p) == 0) {
dat(p) = dat(lc(p));
rt[p] = rt[lc(p)];
return;
}
if(dat(lc(p)) >= dat(rc(p))) {
dat(p) = dat(lc(p));
rt[p] = rt[lc(p)];
}
else {
dat(p) = dat(rc(p));
rt[p] = rt[rc(p)];
}
}
int insert(int p, int l, int r, int pos, int d) {
if(!p) p = ++tot;
if(l == r) {
dat(p) += d;
rt[p] = pos;
return p;
}
int mid = (l + r) >> 1;
if(pos <= mid)
lc(p) = insert(lc(p), l, mid, pos, d);
else
rc(p) = insert(rc(p), mid + 1, r, pos, d);
push(p);
return p;
}
int merge(int p, int q, int l, int r) {
if(!p) return q;
if(!q) return p;
if(l == r) {
dat(p) += dat(q);
return p;
}
int mid = (l + r) >> 1;
lc(p) = merge(lc(p), lc(q), l, mid);
rc(p) = merge(rc(p), rc(q), mid + 1, r);
push(p);
return p;
}
int n;
int Log2[maxn], fa[maxn][30], dep[maxn];
bool vis[maxn];
int head[maxn];
int p;
struct Edge {
int to, dis = 1, next;
}edge[maxm];
void dfs(int cur = 1, int fath = 0) {
if(vis[cur]) return;
vis[cur] = true;
dep[cur] = dep[fath] + 1;
fa[cur][0] = fath;
for(int i = 1; i <= Log2[dep[cur]]; i++)
fa[cur][i] = fa[fa[cur][i-1]][i-1];
for(int i = head[cur]; i; i = edge[i].next)
dfs(edge[i].to, cur);
}
int LCA(int a, int b) {
if(dep[a] > dep[b])
swap(a, b);
while(dep[a] != dep[b])
b = fa[b][Log2[dep[b]-dep[a]]];
if(a == b)
return a;
for(int k = Log2[dep[a]]; k >= 0; k--) //跳跃长度从长到短
if(fa[a][k] != fa[b][k]) {
a = fa[a][k];
b = fa[b][k];
}
return fa[a][0];
}
void init() {
for(int i = 1; i <= n; i++) {
dep[i] = 0;
head[i] = 0;
}
p = 0;
for(int i = 2; i <= n; i++)
Log2[i] = Log2[i / 2] + 1;
}
void add_edge(int u, int v, int w = 1) {
p++;
edge[p].to = v;
edge[p].dis = w;
edge[p].next = head[u];
head[u] = p;
}
int r = 0;
int ans[maxn];
void calc(int u, int fa) {
for(int i = head[u]; i; i = edge[i].next) {
int v = edge[i].to;
if(v == fa) continue;
calc(v, u);
rt[u] = merge(rt[u], rt[v], 1, r);
}
ans[u] = rt[rt[u]];
if(dat(rt[u]) == 0) ans[u] = 0;
}
void solve() {
tot = 0;
int m;
cin >> n >> m;
init();
for(int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
add_edge(u, v);
add_edge(v, u);
}
dfs();
for(int i = 1; i <= m; i++) {
cin >> x[i] >> y[i] >> z[i];
r = max(r, z[i]);
}
for(int i = 1; i <= m; i++) {
int lca = LCA(x[i], y[i]);
rt[x[i]] = insert(rt[x[i]], 1, r, z[i], 1);
rt[y[i]] = insert(rt[y[i]], 1, r, z[i], 1);
rt[lca] = insert(rt[lca], 1, r, z[i], -1);
if(fa[lca][0])
rt[fa[lca][0]] = insert(rt[fa[lca][0]], 1, r, z[i], -1);
}
calc(1, 0);
for(int i = 1; i <= n; i++)
cout << ans[i] << endl;
}