HDU 3966 Aragorn's Story(模板题)树链剖分+线段树
Posted 00isok
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 3966 Aragorn's Story(模板题)树链剖分+线段树相关的知识,希望对你有一定的参考价值。
<题目链接>
题目大意:
给定一颗带点权的树,进行两种操作,一是给定树上一段路径,对其上每个点的点权增加或者减少一个数,二是对某个编号点的点权进行查询。
解题分析:
树链剖分的模板题,还不会树链剖分可以看这里 >>>
#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <vector> #pragma comment(linker, "/STACK:1024000000,1024000000") //手动扩栈 using namespace std; typedef long long ll; const int N=50005; int siz[N],top[N],son[N],dep[N],tid[N],rk[N],fa[N]; //siz[]数组,用来保存以x为根的子树节点个数 //top[]数组,用来保存当前节点的所在链的顶端节点 //son[]数组,用来保存重儿子 //dep[]数组,用来保存当前节点的深度 //fa[]数组,用来保存当前节点的父亲 //tid[]数组,用来保存树中每个节点剖分后的新编号 //rk[]数组,线段树中编号对应的原节点编号 #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 int sum[4*N],lazy[4*N]; int cnt,tim,n,m,p,a[N]; struct EDGE { int next,to; }edge[N*2]; int head[N]; void init() { cnt=tim=0; memset(son,-1,sizeof(son)); memset(head,-1,sizeof(head)); } void add(int u,int v) { edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; } void dfs1(int u,int pre/*fa*/,int de) /*求出每个点的深度和父节点,和重儿子编号*/ { dep[u]=de;fa[u]=pre;siz[u]=1; for(int i=head[u]; ~i; i=edge[i].next) { int v=edge[i].to; if(v!=pre) { dfs1(v,u,de+1); siz[u]+=siz[v]; if(son[u]==-1||siz[v]>siz[son[u]]) son[u]=v; //得到u的重儿子 } } } void dfs2(int u,int tp) /*给链编号,形成一一对应的关系*/ { top[u]=tp; //将该节点所在链的顶端置为tp tid[u]=++tim; //用来保存树中每个节点剖分后的新编号 rk[tid[u]]=u; //线段树中编号对应的原节点编号 if(son[u]==-1) return; dfs2(son[u],tp); //这里用来连接重链 for(int i=head[u]; ~i; i=edge[i].next) { int v=edge[i].to; if(v!=son[u]/*不是u的重儿子*/&&v!=fa[u]/*不是u的父节点*/) dfs2(v,v); //不能加入当前重链上的节点,以该节点为链首,向下拉一条新的重链 } } void Pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void Pushdown(int rt,int len) //将懒惰标记下放 { if(lazy[rt]) { lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; sum[rt<<1]+=lazy[rt]*(len-(len>>1)); sum[rt<<1|1]+=lazy[rt]*(len>>1); lazy[rt]=0; } } void build(int l,int r,int rt) { lazy[rt]=0; if(l==r) { sum[rt]=a[rk[l]];//初始值为原编号的数量 return; } int mid=(l+r)>>1; build(lson); build(rson); Pushup(rt); } void update(int L,int R,int val,int l,int r,int rt) //线段树区间修改 { if(L<=l&&r<=R) { lazy[rt]+=val; sum[rt]+=val*(r-l+1); return; } Pushdown(rt,r-l+1); int mid=(l+r)>>1; if(mid>=L) update(L,R,val,lson); if(mid<R) update(L,R,val,rson); Pushup(rt); } int query(int pos,int l,int r,int rt) //单点查询 { if(l==r){ return sum[rt]; } Pushdown(rt,r-l+1); int mid=(l+r)>>1; int ans=0; if(pos<=mid) ans+=query(pos,lson); else ans+=query(pos,rson); return ans; } /* 如果在同一条链上,那么他们在线段树中的坐标一定是连续的 如果不在一条链上,那么比较x,y点链顶的父节点深度,更新深度深的链(进行交换,把深度深的赋予为x),更新完后,将范围调整到y--fa[top[x]],然后一直迭代,直到在同一个链上。 */ void Change(int x,int y,int val) { while(top[x]!=top[y]) //如果x和y不在同一条链上 { if(dep[top[x]]<dep[top[y]]) swap(x,y); //把深度更深的赋给x update(tid[top[x]],tid[x],val,1,n,1); x=fa[top[x]]; } if(dep[x]>dep[y])swap(x,y); //改成 if(tid[x]>tid[y])swap(x,y); 也可以 update(tid[x],tid[y],val,1,n,1); } int main() { while(~scanf("%d%d%d",&n,&m,&p)) { init(); for(int i=1; i<=n; i++) scanf("%d",&a[i]); while(m--) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs1(1,0,0); /*求出每个点的深度和父节点,和重儿子编号*/ dfs2(1,1); /*给链编号,形成一一对应的关系*/ build(1,n,1); //用线段树来维护剖分出的树链 while(p--) { char oper[2]; int aa,b,c; scanf("%s",oper); if(oper[0]==‘Q‘) { scanf("%d",&aa); printf("%d ",query(tid[aa],1,n,1)); } else { scanf("%d%d%d",&aa,&b,&c); if(oper[0]==‘D‘) c=-c; Change(aa,b,c); } } } }
2018-09-09
以上是关于HDU 3966 Aragorn's Story(模板题)树链剖分+线段树的主要内容,如果未能解决你的问题,请参考以下文章
AC日记——Aragorn's Story HDU 3966
HDU - 3966 Aragorn's Story(树链剖分入门+线段树)