[luoguP4556] [Vani有约会] 雨天的尾巴
题意
给定 \(n\) 个点的无根树,进行 \(m\) 次操作,每次使 \(x\to y\) 路径上的每个点的可重集合内都插入一个 \(z\),求每个点的可重集合内最多的数是多少,数量相同输出最小的。
sol
路径操作、离线,因此可以想到树上差分。
开一个数组,记录每个点的可重集合内每个数的个数,然后做树上差分。最后求和之后统计即可,但是这样做求和后统计是 \(O(n\max z)\) 的。
考虑在一遍做树上差分时一边计算,这样最后可以一边求和一边记录答案了。可以将每个点上的数组变为动态开点线段树,这样就可以一遍计算一遍做树上差分了,但需要在求和时合并线段树
线段树合并
与 FHQ-Treap 的合并操作类似(见[lnsyoj285/luoguP2596/ZJOI2006]书架)。
如果存在一个位置只有一棵树存在,那么直接把这个点连过去,否则就向下遍历。当遍历到叶子节点时,将这两个节点合并,再将数据上推。这样就合并了两棵线段树。
注意数组要开够,数的个数只有大于 \(0\) 时才可以作为答案输出。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100005, M = N * 2, INF = 100000;
int rt[N];
int node_cnt;
int h[N], e[M], ne[M], idx;
int n, m;
int fa[N][20], dep[N];
int ans[N];
struct Node {
int val = 0, id;
int l = 0, r = 0;
} tr[N * 20 * 4];
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void dfs_init(int u, int father){
dep[u] = dep[father] + 1;
fa[u][0] = father;
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == father) continue;
dfs_init(j, u);
}
}
int lca(int x, int y){
if (dep[x] < dep[y]) swap(x, y);
for (int i = 17; i >= 0; i -- ){
int fax = fa[x][i];
if (dep[fax] >= dep[y]) x = fax;
}
if (x == y) return x;
for (int i = 17; i >= 0; i -- ){
int fax = fa[x][i], fay = fa[y][i];
if (fax != fay) x = fax, y = fay;
}
return fa[x][0];
}
void pushup(int u){
if (tr[tr[u].l].val >= tr[tr[u].r].val) {
tr[u].val = tr[tr[u].l].val;
tr[u].id = tr[tr[u].l].id;
}
else {
tr[u].val = tr[tr[u].r].val;
tr[u].id = tr[tr[u].r].id;
}
}
void update(int &u, int l, int r, int x, int val){
if (!u) u = ++ node_cnt;
if (l == r) {
tr[u].val += val;
tr[u].id = x;
return ;
}
int mid = l + r >> 1;
if (x <= mid) update(tr[u].l, l, mid, x, val);
else update(tr[u].r, mid + 1, r, x, val);
pushup(u);
}
int merge(int x, int y, int l, int r){
if (!x || !y) return x | y;
if (l == r) {
tr[x].val += tr[y].val;
return x;
}
int mid = l + r >> 1;
tr[x].l = merge(tr[x].l, tr[y].l, l, mid);
tr[x].r = merge(tr[x].r, tr[y].r, mid + 1, r);
pushup(x);
return x;
}
void dfs(int u, int father){
for (int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (j == father) continue;
dfs(j, u);
rt[u] = merge(rt[u], rt[j], 1, INF);
}
ans[u] = tr[rt[u]].val > 0 ? tr[rt[u]].id : 0;
}
void debug(int u, int L, int R){
printf("##%d %d %d %d %d %d %d\n", u, tr[u].l, tr[u].r, L, R, tr[u].val, tr[u].id);
int mid = L + R >> 1;
if (tr[u].l) debug(tr[u].l, L, mid);
if (tr[u].r) debug(tr[u].r, mid + 1, R);
}
int main(){
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
for (int i = 1; i < n; i ++ ){
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs_init(1, 0);
for (int k = 1; k < 18; k ++ )
for (int u = 1; u <= n; u ++ )
fa[u][k] = fa[fa[u][k - 1]][k - 1];
while (m -- ){
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
int l = lca(x, y);
update(rt[x], 1, INF, z, 1);
update(rt[y], 1, INF, z, 1);
update(rt[l], 1, INF, z, -1);
if (fa[l][0]) update(rt[fa[l][0]], 1, INF, z, -1);
}
dfs(1, -1);
for (int i = 1; i <= n; i ++ ) printf("%d\n", ans[i]);
// debug(rt[1], 1, INF);
}