题目链接:AcWing 353
题意:一颗树,m次操作,x,y,z,每次给x到y的链上的所有点都增加一个种类为z的物品,问最后每个点上最多的物品是什么,若有多个物品相同,则输出编号最小的。
数据范围:
n
,
m
<
=
1
0
5
,
1
<
=
z
<
=
1
0
9
n,m<=10^5,1<=z<=10^9
n,m<=105,1<=z<=109
这里直接给出一个普适的方法。
给链x->y的点加一个数字z。
一般是在x,y处+z,在lca(x,y)处-z,在father(lca)处-z。
对于边的差分,用一条边的儿子表示这条边,那么考虑经过x—>y的边的次数,差分表示为 x,y处+1,lca(x,y)处—2即可,在边的差分里,树的根节点是无意义的存在。
那么一次操作就可以拆为4次单点加减,先考虑一个MLE的做法,也即对于每个点都开一个权值线段树来记录该点上出现的数字最多的是谁。
为了避免MLE,就需要用线段树的动态开点了,最开始我们建了一颗空的线段树,每次需要用到什么点,我们就为他动态的开一个点出来。
做完所有点操作后,一次dfs,统计答案就需要将一个点与他的子树中点的线段树合并起来。就像树状数组那种差分的形式,要询问一个点的权值,是不是需要将覆盖过他的修改都加起来,这里线段树的合并起的作用与之一致。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
int b[maxn],m;
void quchong(int n){
sort(b+1,b+1+n);
m=unique(b+1,b+1+n)-b-1;
}
int getid(int x){
return lower_bound(b+1,b+1+m,x)-b;
}
int head[maxn],top;
struct Edge{
int v,next;
}edge[maxn<<1];
void init(){
top=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v){
edge[top].v=v;
edge[top].next=head[u];
head[u]=top++;
}
int tot;
struct Tree{
int lc,rc;
int maxx,id;
}a[maxn*50];
int root[maxn];
//动态开点;
int build(){
int k=++tot;
a[k].lc=a[k].rc=0;
a[k].maxx=a[k].id=0;
return k;
}
//更新;
void insert(int p,int l,int r,int id,int val){
if(l==r){
a[p].maxx+=val;
if(a[p].maxx==0) a[p].id=0;
else a[p].id=l;
return ;
}
int mid=(l+r)>>1;
if(id<=mid){
if(!a[p].lc) a[p].lc=build();
insert(a[p].lc,l,mid,id,val);
}
else{
if(!a[p].rc) a[p].rc=build();
insert(a[p].rc,mid+1,r,id,val);
}
// 当有多个最大值出现时,需要选取编号更小的,所以这里是>=;
if(a[a[p].lc].maxx>=a[a[p].rc].maxx) a[p].maxx=a[a[p].lc].maxx,a[p].id=a[a[p].lc].id;
else a[p].maxx=a[a[p].rc].maxx,a[p].id=a[a[p].rc].id;
}
//将根节点为q的线段树的信息合并到p上;
int marge(int p,int q,int l,int r){
if(!p) return q;
if(!q) return p;
if(l==r){
a[p].maxx+=a[q].maxx;
if(a[p].maxx) a[p].id=l;
else a[p].id=0;
return p;
}
int mid=(l+r)>>1;
a[p].lc=marge(a[p].lc,a[q].lc,l,mid);
a[p].rc=marge(a[p].rc,a[q].rc,mid+1,r);
if(a[a[p].lc].maxx>=a[a[p].rc].maxx) a[p].maxx=a[a[p].lc].maxx,a[p].id=a[a[p].lc].id;
else a[p].maxx=a[a[p].rc].maxx,a[p].id=a[a[p].rc].id;
return p;
}
int fa[maxn][29];
const int ci=20;
queue<int> q;
int dep[maxn];
void bfs(int st){
q.push(st);
dep[st]=1;
int u,v;
while(!q.empty()){
u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].v;
if(dep[v]) continue;
dep[v]=dep[u]+1;
fa[v][0]=u;
for(int j=1;j<=ci;++j)
fa[v][j]=fa[fa[v][j-1]][j-1];
q.push(v);
}
}
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=ci;i>=0;--i)
if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=ci;i>=0;--i)
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void updata(int x,int y,int z){
int w=LCA(x,y);
z=getid(z);
insert(root[x],1,m,z,1);
insert(root[y],1,m,z,1);
insert(root[w],1,m,z,-1);
if(fa[w][0]) insert(root[fa[w][0]],1,m,z,-1);
}
bool vis[maxn];
int ans[maxn];
void dfs(int u){//统计答案;
int v;
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].v;
if(vis[v]) continue;
dfs(v);
root[u]=marge(root[u],root[v],1,m);
}
ans[u]=b[a[root[u]].id];
}
struct Node{
int l,r,w;
}c[maxn];
int main(){
int n,q,u,v,w;
init();
scanf("%d%d",&n,&q);
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
tot=0;
for(int i=1;i<=n;++i) root[i]=build();
bfs(1);
for(int i=1;i<=q;++i){
scanf("%d%d%d",&c[i].l,&c[i].r,&c[i].w);
b[i]=c[i].w;
}
quchong(q);
for(int i=1;i<=q;++i) updata(c[i].l,c[i].r,c[i].w);
dfs(1);
for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
return 0;
}