树点分治与树链刨分
来源:互联网 发布:软件评测师视频 编辑:程序博客网 时间:2024/06/08 12:27
树分治:
树分治是一种解决树上路径问题的一种神奇方法.
它可以把复杂度为
假如,一颗树长得很正常,那么它的深度大约为
但是,如果一棵树长得很畸形(比如一条链),那深度就是n了.
如果对于每一个子树,可以随便选择根节点的话,那就不必要选择原来畸形的树根而去选择树的重心来保证深度.
* 树的重心:
* 树中一个最大子树 大小(size) 最小的点
* 树的重心的最大子树的size(mxsize)
* 证明:
* 设重心为x,最大子树为t.
* 假设size>SIZE/2,
* 那么如果重心为t,
mxsize为 size-1,size-1
* 所以x不是重心.
* 所以重心的mxsize小于SIZE/2.
所以,这样复杂度就从
树链刨分:
(由于我只会轻重路径刨分,所以只讨论轻重路径刨分)
- 可以把树分为若干重路径和轻边.
- 从点v向size最大的点u连的一条边为重边,其余为轻边.
- 全为重边构成的路径为重路径.
- 它有三个性质:
- 若
e(v,to) 为轻边,size(to)<= size(v)/2. - 从根到任意点只有
logn 条轻边:- 由于size(to)
<= size(v)/2. - 所以经过最多
logn 条size就会变为1. - 所以最多
logn 条轻边.
- 由于size(to)
- 从根到任意点只有
logn 条重边:- n条重边由n-1条轻边连接.
- 从根到任意点只有
logn 条轻边. - 所以从根到任意点只有
logn 条重边.
所以树链刨分的各种操作(不使用其他数据结构)的复杂度都为logn .
- 若
树链刨分的用途:
1.LCA:
- 每个点记录 fa[x] 和 top[x] (链的端点).
- 若top[x]=top[y],说明x,y在同一条链上.LCA为深度较小的一个点.
- 否则,使 fa[top[x]]深度大的点跳到fa[top[x]]
#include <cstdio>#include <cstdlib>#include <vector>#include <iostream>#include <algorithm>#define debug(x) cout<<#x<<" : "<<x<<endlusing namespace std;const int M=100005;template <class T> void Rd(T &res) { res=0; char c; while (c=getchar(),!isdigit(c)); do res=(res<<1)+(res<<3)+(c^48); while (c=getchar(),isdigit(c));}struct node { int to; node *nxt;};struct graph { node *st[M]; void add(int x,int y) { node *now=new node; now->to=y; now->nxt=st[x]; st[x]=now; } int sz[M],fa[M],top[M],dep[M]; void dfs(int x,int f,int d) { sz[x]=1,fa[x]=f,dep[x]=d; for (node *i=st[x];i;i=i->nxt) { int to=i->to; if (to==f) continue; dfs(to,x,d+1); sz[x]+=sz[to]; } } void rdfs(int x,int f,int tp) { top[x]=tp; int id=0; for (node *i=st[x];i;i=i->nxt) { int to=i->to; if (to==f) continue; if (sz[to]>sz[id]) id=to; } for (node *i=st[x];i;i=i->nxt) { int to=i->to; if (to==f) continue; if (to==id) rdfs(to,x,tp); else rdfs(to,x,to); } } int LCA(int x,int y) { if (dep[x]>dep[y]) swap(x,y); if (top[x]==top[y]) return x; int nxtx=fa[top[x]]; int nxty=fa[top[y]]; if (dep[nxtx]>dep[nxty]) return LCA(nxtx,y); else return LCA(x,nxty); }} g;int main() { int n,m; Rd(n),Rd(m); for (int i=1,x,y;i<n;++i) { Rd(x),Rd(y); g.add(x,y); g.add(y,x); } g.dfs(1,1,1); g.rdfs(1,1,1); for (int i=1,x,y;i<=m;++i) { Rd(x),Rd(y); printf("%d\n",g.LCA(x,y)); } return 0;}
2.关于树形态不变,而边权点权改变的问题.
- 在dfs刨分时,记录id,让重链的编号连续.
- 用线段树维护编号对应权值.
struct seg_tree { int add[M<<2]; void clear() { memset(add,0,sizeof(add)); } void down(int p) { add[p<<1]+=add[p]; add[p<<1|1]+=add[p]; add[p]=0; } void update(int x,int st,int ed,int L=1,int R=M-1,int p=1) { if (st==L&&ed==R) { add[p]+=x; return; } int mid=(L+R)>>1; if (ed<=mid) update(x,st,ed,L,mid,p<<1); else if (st>mid) update(x,st,ed,mid+1,R,p<<1|1); else update(x,st,mid,L,mid,p<<1),update(x,mid+1,ed,mid+1,R,p<<1|1); } int query(int x,int L=1,int R=M-1,int p=1) { if (L==R) return add[p]; down(p); int mid=(L+R)>>1; if (x<=mid) return query(x,L,mid,p<<1); else return query(x,mid+1,R,p<<1|1); }};struct node { int to; node *nxt;};struct graph { node *st[M]; seg_tree tree; void add(int x,int y) { node *now=new node; now->to=y,now->nxt=st[x],st[x]=now; } int sz[M],fa[M],top[M],dep[M],ID[M]; void dfs(int x,int f,int d) { sz[x]=1,fa[x]=f,dep[x]=d; for (node *i=st[x];i;i=i->nxt) { if (i->to==f) continue; dfs(i->to,x,d+1); sz[x]+=sz[i->to]; } } int tot; void rdfs(int x,int f,int tp) { ID[x]=++tot; top[x]=tp; int id=0; for (node *i=st[x];i;i=i->nxt) { if (i->to==f) continue; if (sz[i->to]>sz[id]) id=i->to; } if (id) rdfs(id,x,tp); for (node *i=st[x];i;i=i->nxt) { if (i->to==f||i->to==id) continue; rdfs(i->to,x,i->to); } } void Init() { tot=0; dfs(1,1,1),rdfs(1,1,1); } void ADD(int k,int x,int y) { if (dep[x]>dep[y]) swap(x,y); if (top[x]==top[y]) { tree.update(k,ID[x],ID[y]); return; } int nxtx=top[x],nxty=top[y]; if (dep[nxtx]>dep[nxty]) { tree.update(k,ID[nxtx],ID[x]); return ADD(k,fa[nxtx],y); } else { tree.update(k,ID[nxty],ID[y]); return ADD(k,x,fa[nxty]); } } int QUE(int x) { return tree.query(ID[x]); } void clear() { tree.clear(); for (int i=0;i<M;++i) { for (node *j=st[i];j;) { node *nxt=j->nxt; delete j; j=nxt; } st[i]=NULL; } }} g;int val[M];void solve(int n,int m,int p) { g.clear(); for (int i=1;i<=n;++i) { Rd(val[i]); } for (int i=1,x,y;i<=m;++i) { Rd(x),Rd(y); g.add(x,y),g.add(y,x); } g.Init(); for (int i=1,a,b,c;i<=p;++i) { char s[10]; scanf("%s",s); if (s[0]=='I'||s[0]=='D') { Rd(a),Rd(b),Rd(c); if (s[0]=='I') g.ADD(c,a,b); else g.ADD(-c,a,b); } else { Rd(a); printf("%d\n",g.QUE(a)+val[a]); } }}
(上面是HDU3966的代码)
3. …………..
0 0
- 树点分治与树链刨分
- 树分治-点分治
- 树的分治-点分治
- 树分治(点分治+边分治)
- 树的点分治
- 树的点分治
- 【jzoj4715】【树上路径】【树】【分治】【点分治】
- POJ1741树的分治之点分治
- poj1741 树的点分治
- 树基于点的分治
- 【树的点分治】阴阳
- poj2114(树的点分治)
- CV1873 树的点分治
- 浅谈树的点分治
- poj1987 树的点分治
- UVA6642(树的点分治)
- bzoj2152 聪聪与可可 点分治
- 点分治
- Mysql基础(学习笔记)
- RBAC新解:基于资源的权限管理(Resource-Based Access Control)
- c++中必须在类初始化列表中初始化的几种情况
- css的基本排版与元素分类简述
- 作为测试,怎么样才能确切知道程序哪有风险呢?
- 树点分治与树链刨分
- java学习笔记之sql常用语句
- 欢迎使用CSDN-markdown编辑器
- 阅读笔记一(Android系统创建桌面快捷方式ShortCut)
- UVA 10564 Paths through Hourglass(dp)
- NSDate那点事(时间处理封装)
- 实现业务系统中的用户权限管理
- Linux下的文件管理函数(不带I/O缓冲)2.0
- android编程取消标题栏方法(appcompat_v7、Theme.NoTitleBar)