隐藏页面特效

Query on a tree——树链剖分整理

树链剖分整理

树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护。

通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点vsize[v]最大的vu的边是重边,其它边是轻边,其中size[v]是以v为根的子树的节点个数,全部由重边组成的路径是重路径,根据论文上的证明,任意一点到根的路径上存在不超过logn条轻边和logn条重路径。

这样我们考虑用数据结构来维护重路径上的查询,轻边直接查询。

通常用来维护的数据结构是线段树,splay较少见。

 

具体步骤

预处理

第一遍dfs

    求出树每个结点的深度dep[x],其为根的子树大小siz[x]

,其重儿子,以及祖先的信息fa[x]表示x的直接父亲,

第二遍dfs

根节点为起点,向下拓展构建重链

选择最大的一个子树的根继承当前重链

其余节点,都以该节点为起点向下重新拉一条重链

搞出top[x]top[x]表示x所在链的端点

给每个结点分配一个位置编号,每条重链就相当于一段区间,用数据结构去维护。

搞出pos[x],pos[x]表示在线段树中以x为下端点的标号(一般不维护边)

把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可

修改操作

1、单独修改一个点的权值    //例题中没用

根据其编号直接在数据结构中修改就行了。

2、修改点u和点v的路径上的权值

1)若uv在同一条重链上

直接用数据结构修改pos[u]pos[v]间的值。

2)若uv不在同一条重链上

一边进行修改,一边将uv往同一条重链上靠,然后就变成了情况(1)。

查询操作

    查询操作的分析过程同修改操作

题目不同,选用不同的数据结构来维护值,通常有线段树和splay

  

例题、SPOJ 305:Query on a tree
题意:10000个点的树,有边权(<=1000000),支持两个操作:
1、CHANGE i ti 把第i条变的权改为ti
2、QUERY a b 查询a,b两点间路径上的最大边
20组数据

 

#include<cstdio> #include<cstring> #include<iostream> #define lc k<<1 #define rc k<<1|1 #define IN inline #define R register using namespace std; const int N=1e4+10; IN int read(){ R int x=0;R bool f=1; R char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return f?x:-x; } struct node{ int u,v,w,next; }e[N<<1]; int n,T,tot,num,head[N],fa[N],top[N],pos[N],dep[N],siz[N],son[N]; int a[N<<2]; void add(int x,int y,int z){ e[++tot].u=x; e[tot].v=y; e[tot].w=z; e[tot].next=head[x]; head[x]=tot; } void dfs(int u,int f,int de){ fa[u]=f;dep[u]=de;siz[u]=1; for(int i=head[u],v;i;i=e[i].next){ v=e[i].v; if(v!=f){ dfs(v,u,de+1); siz[u]+=siz[v]; if(!son[u]||siz[son[u]]<siz[v]){ son[u]=v; } } } } void getpos(int u,int tp){ top[u]=tp; pos[u]=++num; if(!son[u]) return ; getpos(son[u],tp); for(int i=head[u],v;i;i=e[i].next){ v=e[i].v; if(v!=son[u]&&v!=fa[u]){ getpos(v,v); } } } void change(int k,int l,int r,int pos,int val){ if(l==r){ a[k]=val; return; } int mid=l+r>>1; if(pos<=mid) change(lc,l,mid,pos,val); else change(rc,mid+1,r,pos,val); a[k]=max(a[lc],a[rc]); } int query(int k,int l,int r,int x,int y){ if(l==x&&y==r) return a[k]; int mid=l+r>>1; if(y<=mid) return query(lc,l,mid,x,y); else if(x>mid) return query(rc,mid+1,r,x,y); else return max(query(lc,l,mid,x,mid),query(rc,mid+1,r,mid+1,y)); } int find(int u,int v){ int tp1=top[u],tp2=top[v],ans=0; while(tp1!=tp2){ if(dep[tp1]<dep[tp2]){ swap(tp1,tp2); swap(u,v); } ans=max(ans,query(1,1,num,pos[tp1],pos[u])); u=fa[tp1];tp1=top[u]; } if(u==v) return ans; if(dep[u]>dep[v]) swap(u,v); return max(ans,query(1,1,num,pos[u]+1,pos[v])); } void Cl(){ tot=0;num=0; memset(a,0,sizeof a); memset(fa,0,sizeof fa); memset(head,0,sizeof head); memset(pos,0,sizeof pos); memset(top,0,sizeof top); memset(son,0,sizeof son); memset(dep,0,sizeof dep); } int main(){ for(T=read();T--;){ Cl(); n=read(); for(int i=1,x,y,z;i<n;i++){ x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } dfs(1,0,1); getpos(1,1); for(int i=1,t=(n-1)*2;i<t;i+=2){ if(dep[e[i].v]<dep[e[i].u]) swap(e[i].u,e[i].v); change(1,1,num,pos[e[i].v],e[i].w); } char ch[20]; for(int x,y;;){ scanf("%s",ch); if(ch[0]=='D') break; if(ch[0]=='C'){ x=read();y=read(); change(1,1,num,pos[e[x*2-1].v],y); } else{ x=read();y=read(); printf("%d\n",find(x,y)); } } } return 0; }

 

 

 


__EOF__

本文作者shenben
本文链接https://www.cnblogs.com/shenben/p/6194491.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   神犇(shenben)  阅读(391)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示