bzoj3307 雨天的尾巴 题解(线段树合并+树上差分)
Description
N个点,形成一个树状结构。有M次发放,每次选择两个点x,y
对于x到y的路径上(含x,y)每个点发一袋Z类型的物品。完成
所有发放后,每个点存放最多的是哪种物品。
Input
第一行数字N,M
接下来N-1行,每行两个数字a,b,表示a与b间有一条边
再接下来M行,每行三个数字x,y,z.如题
Output
输出有N行
每i行的数字表示第i个点存放最多的物品是哪一种,如果有
多种物品的数量一样,输出编号最小的。如果某个点没有物品
则输出0
看到对树上路径进行操作,(以蒟蒻博主目前的知识水平)无非两个选择,树剖/树上差分
但是最后只询问一次的话差分会是更好的选择
而且我们要进行的操作并非只是对点的权值进行更改,而是对于每个点插入新的权值并统计出众数
这时候应该想到在每一个节点建权值线段树来维护信息
这样差分的操作就利用权值线段树的插入操作解决了
然鹅在最后统计的时候要合并子树信息
所以还需要线段树合并
#include<cstdio> #include<iostream> #include<cstring> using namespace std; const int N=100005; int n,m,to[N<<1],nxt[N<<1],head[N],tot=0; int size[N*50],type,root[N],ls[N*50],rs[N*50],fa[N][22],dep[N],now[N*50],ans[N]; void add(int x,int y) { to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } void dfs(int x,int deep) { dep[x]=deep; for(int i=1;i<=20;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=head[x];i;i=nxt[i]) { if(dep[to[i]])continue; fa[to[i]][0]=x; dfs(to[i],deep+1); } } int lca(int x,int y) { if(dep[x]>dep[y])swap(x,y); for(int i=20;i>=0;i--) if(dep[fa[y][i]]>=dep[x])y=fa[y][i]; if(x==y)return x; for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i]; return fa[x][0]; } void up(int x) { if(size[ls[x]]>=size[rs[x]]) { size[x]=size[ls[x]]; now[x]=now[ls[x]]; } else { size[x]=size[rs[x]]; now[x]=now[rs[x]]; } } void update(int &k,int l,int r,int val,int num) { if(!k)k=++type; if(l==r) { size[k]+=num; now[k]=l; return ; } int mid=l+r>>1; if(val<=mid)update(ls[k],l,mid,val,num); else update(rs[k],mid+1,r,val,num); up(k); return ; } int Merge(int x,int y,int l,int r) { if(!x||!y)return x+y; if(l==r) { size[x]=size[x]+size[y]; now[x]=l; return x; } int mid=l+r>>1; ls[x]=Merge(ls[x],ls[y],l,mid); rs[x]=Merge(rs[x],rs[y],mid+1,r); up(x); return x; } void cacl(int x) { for(int i=head[x];i;i=nxt[i]) { if(to[i]==fa[x][0])continue; cacl(to[i]); root[x]=Merge(root[x],root[to[i]],1,N-5); } if(now[root[x]])ans[x]=now[root[x]]; else ans[x]=0; } int main() { n=read();m=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); add(x,y);add(y,x); } dfs(1,1); for(int i=1;i<=m;i++) { int x=read(),y=read(),z=read(); int LCA=lca(x,y);//cout<<"///"<<LCA<<endl; update(root[x],1,N-5,z,1); update(root[y],1,N-5,z,1); update(root[LCA],1,N-5,z,-1); update(root[fa[LCA][0]],1,N-5,z,-1); } cacl(1); for(int i=1;i<=n;i++)printf("%d\n",ans[i]); return 0; }
兴许青竹早凋,碧梧已僵,人事本难防。