黑暗爆炸 3307. 雨天的尾巴

链接

https://darkbzoj.tk/problem/3307

题意

树形图上每次分配给 $a$ 到 $b$ 路径上的所有点一个 $z$ 类物品,分配 $m$ 次,求每个点拥有最多的物品的种类

思路

树上差分+权值线段树合并

设 $c$ 数组为每个节点拥有每类物品的计数数组

设 $b$ 数组是差分后的计数数组

对于每次分配,让 $b[x][z]$ 加 $1$ ,$b[y][z]$ 加 $1$ ,$b[lca(x,y)][z]$ 减 $1$ ,$b[fa[lca(x,y)]][z]$ 减 $1$

对于每个点都建立一颗权值线段树,维护 $b$ 数组

而每个点的 $c$ 数组:

$c[x]=b[x]+\sum \limits_{i \in son(x)} c[i]$

自上向下合并线段树即可算出 $c$ 数组

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int cnt,to[N*2],nxt[N*2],head[N];
int x[N],y[N],z[N];
int tot,mp[N],f[N][20],tt,depth[N],st[N],ans[N];
struct seg {
int l=0,r=0;
int mx=0;
int id=0;
}tr[N*50];
int root[N],t;
void addedge(int a,int b) {
cnt++;
to[cnt]=b;
nxt[cnt]=head[a];
head[a]=cnt;
}
void pushup(int p) {
int l=tr[p].l,r=tr[p].r;
if(tr[l].mx>=tr[r].mx) tr[p].mx=tr[l].mx,tr[p].id=tr[l].id;
else tr[p].mx=tr[r].mx,tr[p].id=tr[r].id;
}
void update(int &p,int l,int r,int x,int d) {
if(!p) p=++t;
if(l==r) {
tr[p].mx+=d;
tr[p].id=l;
return;
}
int mid=l+r>>1;
if(x<=mid) update(tr[p].l,l,mid,x,d);
else update(tr[p].r,mid+1,r,x,d);
pushup(p);
}
int merge(int p,int q,int l,int r) {
if(!p) return q;
if(!q) return p;
if(l==r) {
tr[p].mx+=tr[q].mx;
tr[p].id=tr[p].mx?l:0;
return p;
}
int mid=l+r>>1;
tr[p].l=merge(tr[p].l,tr[q].l,l,mid);
tr[p].r=merge(tr[p].r,tr[q].r,mid+1,r);
pushup(p);
return p;
}
void bfs() {
queue<int>q;
q.push(1);
depth[1]=1;
while(q.size()) {
int u=q.front();
q.pop();
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(depth[v]) continue;
depth[v]=depth[u]+1;
f[v][0]=u;
for(int i=1;i<=tt;i++) f[v][i]=f[f[v][i-1]][i-1];
q.push(v);
}
}
}
int lca(int a,int b) {
if(depth[a]>depth[b]) swap(a,b);
for(int i=tt;i>=0;i--) if(depth[f[b][i]]>=depth[a]) b=f[b][i];
if(a==b) return a;
for(int i=tt;i>=0;i--) if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
return f[a][0];
}
void dfs(int u) {
st[u]=-1;
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
if(st[v]==-1) continue;
dfs(v);
root[u]=merge(root[u],root[v],1,tot);
}
ans[u]=mp[tr[root[u]].id];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
tt=(int)log(n)/log(2)+1;
for(int i=1;i<=n;i++) root[i]=++t;
for(int i=1;i<n;i++) {
int a,b;
cin>>a>>b;
addedge(a,b);
addedge(b,a);
}
bfs();
for(int i=1;i<=m;i++) {
cin>>x[i]>>y[i]>>z[i];
mp[++tot]=z[i];
}
sort(mp+1,mp+1+tot);
tot=unique(mp+1,mp+1+tot)-mp-1;
for(int i=1;i<=m;i++) {
int r=lca(x[i],y[i]);
int w=lower_bound(mp+1,mp+1+tot,z[i])-mp;
update(root[x[i]],1,tot,w,1);
update(root[y[i]],1,tot,w,1);
update(root[r],1,tot,w,-1);
if(f[r][0]) update(root[f[r][0]],1,tot,w,-1);
}
dfs(1);
for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
return 0;
}