题意
给定一棵 n n n 个节点的树和 m m m 次操作,每次操作把 u u u 到 v v v 路径上的节点加上一个颜色,最后询问每个点最多颜色的编号(如果相同取编号最小)
1 ≤ n , m ≤ 1 0 5 , 1 ≤ z ≤ 1 0 5 1 \le n,m \le 10^5,1\le z \le 10^5 1≤n,m≤105,1≤z≤105
分析:
此题是线段树合并模板题,这里给出树链剖分的做法。
每次操作修改树上的路径,可以用树链剖分维护一下,注意到
z
z
z 的范围是
1
0
5
10^5
105 ,所以我们不能在树上的每个节点上开一个桶记录颜色,所以可以用权值线段树的动态开点。不过这里有更优做法,因为树链剖分出来的序列对应树上的唯一路径,所以题目的操作就相当于:给定一个序列,每次在
[
l
,
r
]
[l,r]
[l,r] 区间添加一个颜色,询问每个点最多颜色的编号。这样就可以用差分的思想,每次在
l
l
l 点
+
1
+1
+1,
r
+
1
r + 1
r+1 点
−
1
-1
−1,我们把
l
l
l 排序,扫描
1
∼
N
1 \sim N
1∼N 的每个点,每次遍历这个点的询问,把对这个点的修改在权值线段树上操作,然后查询一下最大的下标。
此题在
acwing
\text{acwing}
acwing 上
z
z
z 的数据范围为
1
0
9
10^9
109 所以最好离散化一下。
代码:
#include <bits/stdc++.h>
#define int long long
#define find(x) lower_bound(num.begin(), num.end(), x) - num.begin()
using namespace std;
const int N = 1e5 + 5, M = N << 1;
int z[N], a[N], u[N], v[N], n, m, h[N], e[M], ne[M], idx, id[N], ans[N], mp[N], cnt, dep[N], Size[N], top[N], fa[N], son[N];
vector<int> x[N], num;
struct SegmentTree {
int l, r, mx, val;
} tr[N << 2];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs1(int u, int father, int depth) {
dep[u] = depth, fa[u] = father, Size[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == father) continue;
dfs1(j, u, depth + 1);
Size[u] += Size[j];
if (Size[son[u]] < Size[j]) son[u] = j;
}
}
void dfs2(int u,int t) {
id[u] = ++ cnt, top[u] = t, mp[cnt] = u;
if (!son[u]) return ;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa[u] || j == son[u]) continue;
dfs2(j, j);
}
}
void pushup(int u) {
if (tr[u << 1].mx >= tr[u << 1 | 1].mx) {
tr[u].mx = tr[u << 1].mx;
tr[u].val = tr[u << 1].val;
} else {
tr[u].mx = tr[u << 1 | 1].mx;
tr[u].val = tr[u << 1 | 1].val;
}
}
void build(int u, int l, int r) {
if (l == r) {
tr[u] = {l, r, 0, l};
} else {
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int pos, int c) {
if (tr[u].l == pos && tr[u].r == pos) {
tr[u].mx += c;
} else {
int mid = tr[u].l + tr[u].r >> 1;
if (pos <= mid) {
modify(u << 1, pos, c);
} else {
modify(u << 1 | 1, pos, c);
}
pushup(u);
}
}
void modify_path(int u, int v, int k) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
x[id[top[u]]].push_back(k), x[id[u] + 1].push_back(-k);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
x[id[v]].push_back(k), x[id[u] + 1].push_back(-k);
}
signed main() {
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 0; i < n - 1; i ++) {
cin >> u[i] >> v[i];
add(u[i], v[i]), add(v[i], u[i]);
}
dfs1(1, -1, 1), dfs2(1, 1);
build(1, 1, N - 1);
for (int i = 1; i <= m; i ++) {
cin >> u[i] >> v[i] >> z[i];
num.push_back(z[i]);
}
sort(num.begin(), num.end());
num.erase(unique(num.begin(), num.end()), num.end());
for (int i = 1; i <= m; i ++) {
modify_path(u[i], v[i], find(z[i]) + 1);
}
for (int i = 1; i < N; i ++) {
for (int j = 0; j < x[i].size(); j ++) {
if (x[i][j] > 0) {
modify(1, x[i][j], 1);
} else {
modify(1, -x[i][j], -1);
}
}
ans[mp[i]] = tr[1].mx ? num[tr[1].val - 1] : 0;
}
for (int i = 1; i <= n; i ++) cout << ans[i] << endl;
}