树链剖分模板+入门题 SPOJ - QTREE
来源:互联网 发布:大数据的理解 编辑:程序博客网 时间:2024/05/16 09:23
题目链接:[点击进入](http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=13013)
树链剖分并不是一个复杂的算法或者数据结构,只是能把一棵树拆成链来处理而已,换一种说法,树链剖分只是xxx数据结构/算法在树上的推广,或者说,树链剖分只是把树hash到了几段连续的区间上。比如说下面这道题,就是将树分为重链和轻链然后映射到线段树上,然后再在线段树上进行查询和修改等操作。所以树链剖分的重点有两个,一是正确的将树分解成几段并映射到线段树上去,二是在线段树中进行查询和修改操作时要注意借助分解后树的性质。
下面这份代码是针对查询修改树上的边的,可以作为模板使用。
一篇讲的非常好的博客:树链剖分
代码如下:
#include<iostream>#include<cstdio>#include<cstring>using namespace std;///基于边权,修改单条边///查询路径边权最大值const int maxn=10010;struct Edge{ int to,next;}edge[maxn*2];int head[maxn],tot;int top[maxn];///top[v]表示v所在链的顶端结点int fa[maxn]; ///父节点int deep[maxn]; ///深度int num[maxn]; ///num[v]表示以v为根的子树的结点树int p[maxn];///p[v]表示v与其父节点的连边在线段树中的位置int fp[maxn]; ///表示线段树中的某个位置对应的边的起始编号int son[maxn]; ///son[u]表示u的重儿子int pos;void init(){ tot=0; memset(head,-1,sizeof(head)); pos=0; memset(son,-1,sizeof(son));}///模拟邻接表void addedge(int u,int v){ edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++;}///第一遍dfs求出fa,num,deep,son等值void dfs1(int u,int pre,int d){ deep[u]=d; fa[u]=pre; num[u]=1; for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(v!=pre) { dfs1(v,u,d+1); num[u]+=num[v]; ///确定u的重儿子 if(son[u]==-1||num[v]>num[son[u]]) son[u]=v; } }}///第二遍dfs求出top和pvoid getpos(int u,int sp){ top[u]=sp; p[u]=pos++; fp[p[u]]=u; if(son[u]==-1) return; ///保证重链上重边在线段树中的连续分布 getpos(son[u],sp); for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(v!=son[u]&&v!=fa[u]) ///对其它不是重边的结点的处理 getpos(v,v); }}///线段树struct node{ int l,r; int Max;}segTree[maxn*3];void build(int i,int l,int r){ segTree[i].l=l; segTree[i].r=r; segTree[i].Max=0; if(l==r) return; int mid=(l+r)/2; build(i<<1,l,mid); build((i<<1)|1,mid+1,r);}void push_up(int i){ segTree[i].Max=max(segTree[i<<1].Max,segTree[(i<<1)|1].Max);}///单点更新k值为valvoid update(int i,int k,int val){ if(segTree[i].l==k&&segTree[i].r==k) { segTree[i].Max=val; return; } int mid=(segTree[i].l+segTree[i].r)/2; if(k<=mid) update(i<<1,k,val); else update((i<<1)|1,k,val); push_up(i);}///区间查询:[l,r]中的最大值int query(int i,int l,int r){ if(segTree[i].l==l&&segTree[i].r==r) return segTree[i].Max; int mid=(segTree[i].l+segTree[i].r)/2; if(r<=mid) return query(i<<1,l,r); else if(l>mid) return query((i<<1)|1,l,r); else return max(query(i<<1,l,mid),query((i<<1)|1,mid+1,r));}///查找u->v边的最大值int find(int u,int v){ int f1=top[u]; int f2=top[v]; int tmp=0; while(f1!=f2) { ///总是保证deep[f1]>=deep[f2] if(deep[f1]<deep[f2]) { swap(f1,f2); swap(u,v); } tmp=max(tmp,query(1,p[f1],p[u])); u=fa[f1]; f1=top[u]; } if(u==v) return tmp; if(deep[u]>deep[v]) swap(u,v); return max(tmp,query(1,p[son[u]],p[v]));}int e[maxn][3];int main(){ int T; int n,u,v; // freopen("in.txt","r",stdin); scanf("%d",&T); while(T--) { init(); scanf("%d",&n); for(int i=0;i<n-1;i++) { scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]); ///注意添加的是双向边 addedge(e[i][0],e[i][1]); addedge(e[i][1],e[i][0]); } dfs1(1,0,0); getpos(1,1); build(1,0,pos-1); ///下面是利用每条边更新线段树的操作 for(int i=0;i<n-1;i++) { if(deep[e[i][0]]>deep[e[i][1]]) swap(e[i][0],e[i][1]); update(1,p[e[i][1]],e[i][2]); } char op[10]; while(scanf("%s",op)) { if(op[0]=='D') break; scanf("%d%d",&u,&v); if(op[0]=='Q') printf("%d\n",find(u,v)); ///查询 else update(1,p[e[u-1][1]],v); ///修改 } } return 0;}
1 1
- 树链剖分模板+入门题 SPOJ - QTREE
- 【SPOJ QTREE】树链剖分模板
- SPOJ 375 QTREE (树链剖分入门题)
- SPOJ QTREE(树链剖分模板)
- 树链剖分(SPOJ-QTREE)(模板)
- SPOJ - QTREE (树链剖分)
- SPOJ QTREE(树链剖分)
- 【高级数据结构】[SPOJ QTREE]树链剖分/动态树各一模板
- Spoj 375 QTREE(树链剖分)
- 树链剖分学习笔记 && SPOJ QTREE
- SPOJ QTREE
- SPOJ QTREE
- SPOJ375 QTREE 树链剖分入门
- Spoj 375 Qtree 树链剖分 + 线段树 解法
- SPOJ 375 QTREE POJ 3237 TREE 树链剖分
- SPOJ QTREE(Query on a tree树链剖分)
- SPOJ QTREE Query on a tree --树链剖分
- [ SPOJ - QTREE]Query on a tree && 树链剖分
- Codeforces 300A Array
- Android应用开发技巧之更方便的使用Sqlite
- 微软面试100题2010年版全部答案集锦(含下载地址)
- JVM项目调优文章-引发思考(精)
- 字符序列 解题报告
- 树链剖分模板+入门题 SPOJ - QTREE
- python字符编码
- ruby on rail 微信企业号回调模式 url初次验证时 echostr的解密
- 前进中不能迷失方向--Java程序员职业发展路线
- pdf阅读器官网
- Swift学习笔记之闭包
- 常用.bash_profile配置
- OC加强第一天--内存管理MRC、@property参数
- ios第三方库类