Address
Solution
- 首先转化一下问题,考虑一种颜色 c c c
- 对于题目给定的一条从 u u u 到 v v v 的颜色为 c c c 的路径
- 在节点 u u u 加上贡献 1 1 1 ,节点 v v v 加上贡献 1 1 1
- 在 u u u 和 v v v 的 LCA 处加上贡献 − 1 -1 −1 , u u u 和 v v v 的 LCA 的父亲节点(如果存在)处加上贡献 − 1 -1 −1
- 那么容易得出,一个点 u u u ,它子树内所有点的贡献之和,就等于点 u u u 被多少条颜色为 c c c 的路径覆盖
- 于是我们可以对于每个点 u u u 用一个 vector ,存下一些二元组 ( c , d e l t a ) (c,delta) (c,delta)
- 表示该点为颜色 c c c 贡献了 d e l t a delta delta ( d e l t a = 1 delta=1 delta=1 或 − 1 -1 −1 )
- 这时候 u u u 点的答案就是 u u u 的子树内,贡献最多的颜色
- 有很多种做法,这里选用线段树合并
- 对每个节点开一棵以颜色为下标的线段树,储存所有颜色的贡献和,维护区间最大值及最大值的位置(最大值不唯一则取编号最小)
- 处理点 u u u 的答案时,先分别处理 u u u 的所有子节点的答案
- 把 u u u 的所有子节点开的线段树合并起来(注意合并到叶子时是相加而不是取 max \max max )
- 然后在合并起来的线段树上再加上 u u u 点的贡献
- 那么点 u u u 的答案就是线段树的全局最大值位置
- 复杂度 O ( n log n ) O(n\log n) O(nlogn)
Code
常数巨大,开了 O2 跑了 395ms
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Tree(u) for (int e = adj[u], v; e; e = nxt[e]) if ((v = go[e]) != fu)
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
template <class T>
inline void Swap(T &a, T &b) {a ^= b; b ^= a; a ^= b;}
const int MAX = 1e5, N = 1e5 + 5, M = N << 1, L = 4e6 + 5, LogN = 20;
int n, m, ecnt, nxt[M], adj[N], go[M], dep[N], ans[N], fa[N][LogN], ToT;
std::vector<int> xin[N], xout[N];
struct node
{
int lc, rc, pos, maxv;
} T[L];
void add_edge(int u, int v)
{
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
void dfs(int u, int fu)
{
int i;
dep[u] = dep[fa[u][0] = fu] + 1;
For (i, 0, 15) fa[u][i + 1] = fa[fa[u][i]][i];
Tree(u) dfs(v, u);
}
int lca(int u, int v)
{
if (dep[u] < dep[v]) Swap(u, v);
int i;
Rof (i, 16, 0)
{
if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
if (u == v) return u;
}
Rof (i, 16, 0)
if (fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
int mer(int l, int r, int x, int y)
{
if (!x || !y) return x ^ y;
int mid = l + r >> 1;
if (l == r)
{
T[x].maxv += T[y].maxv;
T[x].pos = T[x].maxv ? l : 0;
return x;
}
T[x].lc = mer(l, mid, T[x].lc, T[y].lc);
T[x].rc = mer(mid + 1, r, T[x].rc, T[y].rc);
if (T[T[x].lc].maxv > T[T[x].rc].maxv || (T[T[x].lc].maxv
== T[T[x].rc].maxv && T[T[x].lc].pos < T[T[x].rc].pos))
T[x].maxv = T[T[x].lc].maxv, T[x].pos = T[T[x].lc].pos;
else T[x].maxv = T[T[x].rc].maxv, T[x].pos = T[T[x].rc].pos;
return x;
}
void change(int l, int r, int pos, int v, int &p)
{
if (!p) p = ++ToT;
if (l == r)
{
if (T[p].maxv += v) T[p].pos = l;
return;
}
int mid = l + r >> 1;
if (pos <= mid) change(l, mid, pos, v, T[p].lc);
else change(mid + 1, r, pos, v, T[p].rc);
if (T[T[p].lc].maxv > T[T[p].rc].maxv || (T[T[p].lc].maxv
== T[T[p].rc].maxv && T[T[p].lc].pos < T[T[p].rc].pos))
T[p].maxv = T[T[p].lc].maxv, T[p].pos = T[T[p].lc].pos;
else T[p].maxv = T[T[p].rc].maxv, T[p].pos = T[T[p].rc].pos;
}
int jiejuediao(int u, int fu)
{
int i, tmp, rt = 0;
Tree(u) rt = mer(1, MAX, rt, jiejuediao(v, u));
tmp = xin[u].size();
For (i, 0, tmp - 1) change(1, MAX, xin[u][i], 1, rt);
tmp = xout[u].size();
For (i, 0, tmp - 1) change(1, MAX, xout[u][i], -1, rt);
return ans[u] = T[rt].pos, rt;
}
int main()
{
int i, x, y, z;
n = read(); m = read();
For (i, 1, n - 1) x = read(), y = read(), add_edge(x, y);
dfs(1, 0);
while (m--)
{
x = read(); y = read(); z = read();
int w = lca(x, y);
xin[x].push_back(z);
xin[y].push_back(z);
xout[w].push_back(z);
if (w > 1) xout[fa[w][0]].push_back(z);
}
jiejuediao(1, 0);
For (i, 1, n) printf("%d\n", ans[i]);
return 0;
}