P4315 月下“毛景树” (树链剖分+边剖分+区间覆盖+区间加+区间最大值)
题目链接:https://www.luogu.org/problem/P4315
题目大意:
有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的。但是这棵“毛景树”有着神奇的魔力,他能改变树枝上毛毛果的个数:
Change k w:将第k条树枝上毛毛果的个数改变为w个。
Cover u v w:将节点u与节点v之间的树枝上毛毛果的个数都改变为w个。
Add u v w:将节点u与节点v之间的树枝上毛毛果的个数都增加w个。 由于毛毛虫很贪,于是他会有如下询问:
Max u v:询问节点u与节点v之间树枝上毛毛果个数最多有多少个。
解题思路:一道边权树剖题,代码很长,调试起来还是有点复杂。
注意一下几点:
1.可以把边权转化为点权,因为每一个孩子节点通向父节点的边是唯一的,所以可以将每个边的边权转到边所连的孩子节点上(可在树剖的第一个dfs中完成)
2.修改一条链上的权值时,要注意链两端的点的lca不能够被修改,因为lca所对应的边权不在这一条链上。
3.Change 操作是修改第k条树枝,k为读入的顺序,而树的存边是双向的,所以要将读入的k乘以二在进行后面的操作。
4.下推标记的时候如果有覆盖标记不要忘了清除加的标记。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=100005; int tot,cnt,head[maxn],n,m,v[maxn]; ll tree[maxn*4],lazy[maxn*4],cov[maxn*4]; int d[maxn],size[maxn],son[maxn],id[maxn],rk[maxn],fa[maxn],top[maxn]; //cov为覆盖标记,lazy为累加标记 struct Edge{ int u,v,w,next; }edge[maxn<<1]; void add(int u,int v,int w){ edge[++tot].v=v; edge[tot].u=u; edge[tot].w=w; edge[tot].next=head[u]; head[u]=tot; } void dfs1(int u,int pre){ d[u]=d[pre]+1; fa[u]=pre; size[u]=1; for(int i=head[u];i!=-1;i=edge[i].next){ int vv=edge[i].v; if(vv!=pre){ dfs1(vv,u); size[u]+=size[vv]; v[vv]=edge[i].w; //边权转为点权 if(size[son[u]]<size[vv]) son[u]=vv; } } } void dfs2(int u,int tp){ top[u]=tp,id[u]=++cnt,rk[cnt]=u; if(son[u]) dfs2(son[u],tp); for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } } void pushup(int rt){ tree[rt]=max(tree[rt<<1],tree[rt<<1|1]); } void pushdown(int l,int r,int rt){ if(cov[rt]!=-1){ cov[rt<<1]=cov[rt<<1|1]=cov[rt]; tree[rt<<1]=tree[rt<<1|1]=cov[rt]; lazy[rt<<1]=lazy[rt<<1|1]=0; //将孩子节点的lazy标记清0 cov[rt]=-1; } if(lazy[rt]){ tree[rt<<1]=tree[rt<<1]+lazy[rt]; tree[rt<<1|1]=tree[rt<<1|1]+lazy[rt]; lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; lazy[rt]=0; } } void build(int l,int r,int rt){ lazy[rt]=0; cov[rt]=-1; if(l==r){ tree[rt]=v[rk[l]]; return; } int mid=l+r>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); pushup(rt); } void update1(int L,int R,int val,int l,int r,int rt){ //区间Cover和Change if(L<=l&&R>=r){ tree[rt]=val; cov[rt]=val; lazy[rt]=0; //将lazy标记清0 return; } int mid=l+r>>1; pushdown(mid-l+1,r-mid,rt); if(mid>=L) update1(L,R,val,l,mid,rt<<1); if(mid<R) update1(L,R,val,mid+1,r,rt<<1|1); pushup(rt); } void update2(int L,int R,int val,int l,int r,int rt){ //区间Add if(L<=l&&R>=r){ tree[rt]+=val; lazy[rt]+=val; return; } int mid=l+r>>1; pushdown(mid-l+1,r-mid,rt); if(mid>=L) update2(L,R,val,l,mid,rt<<1); if(mid<R) update2(L,R,val,mid+1,r,rt<<1|1); pushup(rt); } ll query(int L,int R,int l,int r,int rt){ //区间求Max if(L<=l&&R>=r) return tree[rt]; int mid=l+r>>1; ll res=0; pushdown(mid-l+1,r-mid,rt); if(mid>=L) res=max(res,query(L,R,l,mid,rt<<1)); if(mid<R) res=max(res,query(L,R,mid+1,r,rt<<1|1)); return res; } void updates1(int x,int y,int val){ //Cover while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]]) swap(x,y); update1(id[top[x]],id[x],val,1,n,1); x=fa[top[x]]; } if(id[x]>id[y]) swap(x,y); update1(id[x]+1,id[y],val,1,n,1); //不能更新lca所以是id[x]+1 } void updates2(int x,int y,int val){ //Add while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]]) swap(x,y); update2(id[top[x]],id[x],val,1,n,1); x=fa[top[x]]; } if(id[x]>id[y]) swap(x,y); update2(id[x]+1,id[y],val,1,n,1); //不能更新lca所以是id[x]+1 } ll ask(int x,int y){ //求Max ll res=0; while(top[x]!=top[y]){ if(d[top[x]]<d[top[y]]) swap(x,y); res=max(res,query(id[top[x]],id[x],1,n,1)); x=fa[top[x]]; } if(id[x]>id[y]) swap(x,y); res=max(res,query(id[x]+1,id[y],1,n,1)); return res; } int main(){ scanf("%d",&n); memset(head,-1,sizeof(head)); cnt=0,tot=0; for(int i=1;i<n;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); add(v,u,w); } dfs1(1,0),dfs2(1,1); build(1,n,1); while(true){ char op[10]; int x,l,r,rt,val; scanf("%s",op); if(op[1]=='t') break; if(op[1]=='h'){ scanf("%d%d",&x,&val); //因为是无向边,所以x*2表示的就是第x条边 x=d[edge[x*2].u]>d[edge[x*2].v]?edge[x*2].u:edge[x*2].v; update1(id[x],id[x],val,1,n,1); //Change操作 } else if(op[1]=='o'){ scanf("%d%d%d",&l,&r,&val); updates1(l,r,val); //Cover操作 } else if(op[1]=='d'){ scanf("%d%d%d",&l,&r,&val); updates2(l,r,val); //Add操作 } else if(op[1]=='a'){ scanf("%d%d",&l,&r); printf("%lld\n",ask(l,r)); //Max操作 } } return 0; }