POJ 3237 Tree (树链剖分+边权转点权)
<题目链接>
题目大意:
给定一棵树,该树带有边权,现在对该树进行三种操作:
一:改变指定编号边的边权;
二:对树上指定路径的边权全部取反;
三:查询树上指定路径的最大边权值。
解题分析:
本题虽然只需要查询某段区间的最大值,但是线段树的每个节点都应该有最大和最小值,因为对区间取反之后,这段区间的最大值的相反数为最小值,最小值的相反数为最大值。然后就是注意对 lazy标记的操作。
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define Lson l,mid,rt<<1 #define Rson mid+1,r,rt<<1|1 using namespace std; const int M = 1e5+7; typedef long long ll; #define INF 1e15 int T,n,pp; int cnt,tot,head[M],p[M]; int sz[M],son[M],dep[M],f[M],top[M],rnk[M],id[M]; ll a[M]; char s[10]; struct edge { int v,next; ll w; }e[M<<1]; struct node { ll mx,mn;int lazy; }tree[M<<2]; void init(){ tot=cnt=0;memset(head,-1,sizeof(head)); } void add(int u,int v,ll w){ e[++cnt].v=v;e[cnt].w=w;e[cnt].next=head[u]; head[u]=cnt; } void fsd(int u,int fa){ //边权转化为点权 for(int i=head[u];~i;i=e[i].next){ int v=e[i].v;ll w=e[i].w; if(v==fa) continue; a[v]=w;p[(i-1)/2+1]=v; fsd(v,u); } } void dfs(int u,int fa,int d){ sz[u]=1;f[u]=fa;son[u]=-1;dep[u]=d; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(v==fa) continue; dfs(v,u,d+1); sz[u]+=sz[v]; if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v; } } void dfs1(int u,int t){ id[u]=++tot; rnk[tot]=u; top[u]=t; if(son[u]==-1) return; dfs1(son[u],t); for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(v==f[u]||v==son[u]) continue; dfs1(v,v); } } void Pushup(int rt){ //维护该点的最大最小值 tree[rt].mx=max(tree[rt<<1].mx,tree[rt<<1|1].mx); tree[rt].mn=min(tree[rt<<1].mn,tree[rt<<1|1].mn); } void Pushdown(int rt){ if(tree[rt].lazy){ int v=tree[rt].lazy; tree[rt].lazy=0; if(v%2==0)return; //如果 lazy是偶数,那么就没必要 将lazy 下放,因为对区间进行偶数次取反,相当于没有进行操作 tree[rt<<1].mn*=-1;tree[rt<<1].mx*=-1; //将左,右子树的mn,mx取反 tree[rt<<1|1].mn*=-1;tree[rt<<1|1].mx*=-1; swap(tree[rt<<1].mn,tree[rt<<1].mx); //交换mx,mn swap(tree[rt<<1|1].mx,tree[rt<<1|1].mn); tree[rt<<1].lazy+=v;tree[rt<<1|1].lazy+=v; //将父节点的lazy传递给子节点 } } void build(int l,int r,int rt){ tree[rt].mx=-INF,tree[rt].mn=INF;tree[rt].lazy=0; //注意最大最小值都要记录,因为一旦取反,最小值就会变成最大值 if(l==r){ tree[rt].mx=tree[rt].mn=a[rnk[l]]; return; } int mid=(l+r)>>1; build(Lson); build(Rson); Pushup(rt); } void update(int L,int R,int l,int r,int rt){ //线段树上区间修改 if(L<=l&&r<=R){ tree[rt].lazy++; //这里来判断区间数值是否需要取是用 lazy%2 == 1 来判断的 tree[rt].mx*=-1;tree[rt].mn*=-1; //因为区间取反,所以mx和mn都 *-1 swap(tree[rt].mx,tree[rt].mn); //因为*-1,所以交换最大最小值 return ; } Pushdown(rt); int mid=(l+r)>>1; if(L<=mid) update(L,R,Lson); if(R>mid) update(L,R,Rson); Pushup(rt); } void change(int l,int r,int rt,int loc,ll v){ //单点修改 if(l==r){ tree[rt].mx=tree[rt].mn=v; //将叶子节点的mx,mn都置为v return; } Pushdown(rt); int mid=(l+r)>>1; if(loc<=mid) change(Lson,loc,v); else change(Rson,loc,v); Pushup(rt); } ll query(int L,int R,int l,int r,int rt){ //区间查询最大值 if(L<=l&&r<=R){ return tree[rt].mx; } Pushdown(rt); int mid=(l+r)>>1; ll res=-INF; if(L<=mid) res=max(res,query(L,R,Lson)); if(R>mid) res=max(res,query(L,R,Rson)); return res; } void updates(int x,int y){ //修改树上区间 int fx=top[x],fy=top[y]; while(fx!=fy){ if(dep[fx]>dep[fy]){ update(id[fx],id[x],1,n,1); //修改线段树上对应区间 x=f[fx],fx=top[x]; } else{ update(id[fy],id[y],1,n,1); y=f[fy],fy=top[y]; } } if(x==y) return; if(dep[x]<dep[y]) update(id[son[x]],id[y],1,n,1); else update(id[son[y]],id[x],1,n,1); } ll sum(int x,int y){ //查询树上区间最大值 int fx=top[x],fy=top[y];ll res=-INF; while(fx!=fy){ if(dep[fx]>dep[fy]){ res=max(res,query(id[fx],id[x],1,n,1)); x=f[fx],fx=top[x]; } else{ res=max(res,query(id[fy],id[y],1,n,1)); y=f[fy],fy=top[y]; } } if(x==y) return res; if(dep[x]<dep[y]) res=max(res,query(id[son[x]],id[y],1,n,1)); else res=max(res,query(id[son[y]],id[x],1,n,1)); return res; } int main(){ scanf("%d",&T); while(T--){ init(); scanf("%d",&n); for(int i=1;i<n;i++){ int u,v;ll w; scanf("%d%d%lld",&u,&v,&w); add(u,v,w);add(v,u,w); } fsd(1,-1);a[1]=0; dfs(1,-1,1); dfs1(1,1); build(1,n,1); while(true){ scanf("%s",s); if(s[0]=='D') break; if(s[0]=='C'){ int a;ll b; scanf("%d%lld",&a,&b); //单点修改 change(1,n,1,id[p[a]],b); //线段树上单点修改 } if(s[0]=='N'){ int a,b; scanf("%d%d",&a,&b); updates(a,b); //对树上的区间进行修改 } if(s[0]=='Q'){ int a,b; scanf("%d%d",&a,&b); printf("%lld\n",sum(a,b)); //对树上的区间查询 } } } return 0; }
2018-09-11
作者:is_ok
出处:http://www.cnblogs.com/00isok/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。