题目大意
给你一棵有 n n n个节点的树和 m m m次操作,每次操作有三个数 x , y , z x,y,z x,y,z,表示在 x x x到 y y y的路径上的每个点放一袋 z z z类型的救济粮。求最后各个点中存放的最多的是哪种救济粮。
1 ≤ n , m ≤ 1 0 5 , 1 ≤ a , b , x , y ≤ n , 1 ≤ z ≤ 1 0 5 1\leq n,m\leq 10^5,1\leq a,b,x,y\leq n,1\leq z\leq 10^5 1≤n,m≤105,1≤a,b,x,y≤n,1≤z≤105
题解
前置知识:线段树合并
首先,对于每个点,建一棵权值线段树,维护该点所有的救济粮的最大值的类型和最大值,位置 k k k存储该节点中第 k k k种救济粮的数量。
对于每次操作,我们可以用树上差分来将其优化到 O ( l o g n ) O(logn) O(logn),具体方法如下:
- 将节点 x , y x,y x,y的线段树中的位置 z z z加一
- 设 x , y x,y x,y的 L C A LCA LCA为 t t t,将 t t t和 t t t的父亲的线段树的位置 z z z减一
- 最后 d f s dfs dfs一次,将所有点的子树与点合并,合并后这个点的线段树维护的就是答案
时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)
code
#include<bits/stdc++.h>
#define N 100000
using namespace std;
int n,m,tot,d[200005],l[200005],r[200005],rt[200005],dep[200005],ans[100005],f[100005][20];
struct node{
int lc,rc,t,mx;
}tr[5000005];
void add(int xx,int yy){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void dfs1(int u,int fa){
dep[u]=dep[fa]+1;
f[u][0]=fa;
for(int i=1;i<=19;i++){
f[u][i]=f[f[u][i-1]][i-1];
}
for(int i=r[u];i;i=l[i]){
if(d[i]==fa) continue;
dfs1(d[i],u);
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--){
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=19;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];y=f[y][i];
}
}
return f[x][0];
}
void up(int k){
if(tr[tr[k].lc].mx>=tr[tr[k].rc].mx){
tr[k].mx=tr[tr[k].lc].mx;
tr[k].t=tr[tr[k].lc].t;
}
else if(tr[tr[k].lc].mx<tr[tr[k].rc].mx){
tr[k].mx=tr[tr[k].rc].mx;
tr[k].t=tr[tr[k].rc].t;
}
}
void pt(int &k,int l,int r,int z,int a){
if(!k) k=++tot;
if(l==r){
tr[k].mx+=a;tr[k].t=z;return;
}
int mid=(l+r)/2;
if(z<=mid) pt(tr[k].lc,l,mid,z,a);
else pt(tr[k].rc,mid+1,r,z,a);
up(k);
}
void merge(int &r1,int r2,int l,int r){
if(!r1||!r2){
r1=r1+r2;return;
}
if(l==r){
tr[r1].mx+=tr[r2].mx;return;
}
int mid=(l+r)/2;
merge(tr[r1].lc,tr[r2].lc,l,mid);
merge(tr[r1].rc,tr[r2].rc,mid+1,r);
up(r1);
}
void dfs2(int u,int fa){
for(int i=r[u];i;i=l[i]){
if(d[i]==fa) continue;
dfs2(d[i],u);
merge(rt[u],rt[d[i]],1,N);
}
if(tr[rt[u]].mx>0) ans[u]=tr[rt[u]].t;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
tot=0;
dfs1(1,0);
for(int i=1,x,y,z,LCA;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
pt(rt[x],1,N,z,1);
pt(rt[y],1,N,z,1);
LCA=lca(x,y);
pt(rt[LCA],1,N,z,-1);
pt(rt[f[LCA][0]],1,N,z,-1);
}
dfs2(1,0);
for(int i=1;i<=n;i++){
printf("%d\n",ans[i]);
}
return 0;
}