题目大意
N个点,形成一个树状结构。有M次发放,每次选择两个点x,y对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成所有发放后,每个点存放最多的是哪种物品。
题解
看到这道题之后我本能的想法就是位置线段树套权值线段树的,但是看了一下,这道题的时间限制和空间限制卡得都比较紧,而且是离线问题,树套树卡起来应该是十分的困难的。
这道题虽然是处理树上问题,但是我们先考虑如何处理这样的区间问题。 如果所有的操作和询问都是在区间上进行的话,如在[x,y]区间内各放置一个z物品,就在x位置打上z物品+1的标记,在y+1位置打上z物品-1的标记。从左到右扫、处理标记,用一棵权值线段树维护。
从区间问题转化到了树上问题,通过这道题我学到了一种新的手段。
每次在一个树上路径上进行修改的时候,如修改x到y的路径的时候,只需要在x和y各打一个z物品+1的标记,在lca和fa[lca]打上z物品-1的标记,这样在dfs递归求解、向上合并的时候就可以做到不重不漏了。
因为在x和y打一个标记,这个标记的作用范围分别是x和y到根的路径(因为被这个标记修改后的线段树会一路被合并到根节点),但是从lca到根的路径上是被这个标记作用了两次的。所以给lca打上z物品-1的标记之后,lca到根的路径这个标记就只作用一次了;再给fa[lca]打上z物品-1的标记,这样这个标记就恰好只覆盖从x到y的路径了。
所以大概的思路就是:
- 处理出各个结点的标记
- 自下向上合并线段树
- 处理标记,修改线段树
- 把答案记录下来
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=int(1e5)+111, inf=int(1e9)+7;
int n,m;
int Tot=0,head[maxn];
struct Edge {
int to,next;
Edge() {}
Edge(int y,int nx):to(y),next(nx) {}
}eage[maxn*2];
inline void add(int x,int y) {
eage[Tot]=Edge(y,head[x]), head[x]=Tot++;
eage[Tot]=Edge(x,head[y]), head[y]=Tot++;
return;
}
int fa[maxn],siz[maxn],dep[maxn],son[maxn];
void dfs1(register int u) {
siz[u]=1, son[u]=0;
for(register int i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u]) {
fa[eage[i].to]=u;
dep[eage[i].to]=dep[u]+1;
dfs1(eage[i].to);
siz[u]+=siz[eage[i].to];
if(siz[eage[i].to]>siz[son[u]]) son[u]=eage[i].to;
}
return;
}
int top[maxn],seq[maxn],id[maxn],ind;
void dfs2(register int u) {
seq[id[u]=++ind]=u;
if(u==son[fa[u]]) top[u]=top[fa[u]];
else top[u]=u;
if(son[u]) dfs2(son[u]);
for(register int i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u] && eage[i].to!=son[u])
dfs2(eage[i].to);
return;
}
inline int LCA(int u,int v) {
while(top[u]!=top[v]) {
if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
vector<pair<int,int> > op[maxn];
#define pb push_back
#define mp make_pair
int v[maxn],vtop;
struct Oper {
int x,y,z;
}q[maxn];
struct Node {
int ls,rs;
pair<int,int> pos;
inline Node() {
ls=rs=0;
pos=mp(0,0);
}
}node[int(2e6)];
int root[maxn],tot=0;
int ans[maxn];
inline void seg_merge(int k1,int k2,int l,int r) {
if(l==r) {
node[k1].pos.first+=node[k2].pos.first;
return;
}
register int &ls=node[k1].ls, &rs=node[k1].rs, mid=(l+r)>>1;
if(node[k2].ls) {
if(!ls) ls=node[k2].ls;
else seg_merge(ls,node[k2].ls,l,mid);
} if(node[k2].rs) {
if(!rs) rs=node[k2].rs;
else seg_merge(rs,node[k2].rs,mid+1,r);
}
if(ls) node[k1].pos=max(node[k1].pos,node[ls].pos);
if(rs) node[k1].pos=max(node[k1].pos,node[rs].pos);
return;
}
inline void seg_modify(int k,int l,int r,int pos,int val) {
if(l==r) {
node[k].pos.first+=val, node[k].pos.second=-pos;
return;
}
register int &ls=node[k].ls, &rs=node[k].rs, mid=(l+r)>>1;
if(pos<=mid) {
if(!ls) ls=++tot;
seg_modify(ls,l,mid,pos,val);
if(!node[ls].pos.first) ls=0;
} else {
if(!rs) rs=++tot;
seg_modify(rs,mid+1,r,pos,val);
if(!node[rs].pos.first) rs=0;
}
if(ls && rs) node[k].pos=max(node[ls].pos,node[rs].pos);
else if(ls) node[k].pos=node[ls].pos;
else if(rs) node[k].pos=node[rs].pos;
else node[k].pos=mp(0,0);
return;
}
inline void dfs(register int u) {
root[u]=++tot;
for(register int i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u]) {
dfs(eage[i].to);
seg_merge(root[u],root[eage[i].to],1,vtop);
}
for(register int i=0;i<(int)op[u].size();i++) {
seg_modify(root[u],1,vtop,op[u][i].first,op[u][i].second);
}
ans[u]=-node[root[u]].pos.second;
return;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input0.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
head[i]=-1;
for(int x,y,i=1;i<n;i++) {
scanf("%d%d",&x,&y);
add(x,y);
}
dfs1(1),dfs2(1);
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);
v[++vtop]=q[i].z;
}
sort(v+1,v+1+vtop);
vtop=unique(v+1,v+1+vtop)-(v+1);
for(int i=1;i<=m;i++) {
int x=q[i].x, y=q[i].y, z=lower_bound(v+1,v+1+vtop,q[i].z)-v;
op[x].pb(mp(z,+1)), op[y].pb(mp(z,+1));
int lca=LCA(x,y);
op[lca].pb(mp(z,-1)), op[fa[lca]].pb(mp(z,-1));
}
dfs(1);
for(int i=1;i<=n;i++) printf("%d\n",v[ans[i]]);
return 0;
}