树链剖分来一发
2016-04-10 17:24
309 查看
好几周之前学长讲了树链剖分(ps:我记得讲的时间超级短,大概不到二十分钟就讲完了~_~,那时候学长说好简单,,,,现在看起来确实不是很难)
HDU 3966
题意:
给了一个树和各个点的权值,不超过五万个点,有以下三种操作,操作数不超过十万
I a b w :(I是字符"I",a,b,w是数字)表示从 a 到 b 的路径上的点的权值都加上 w
D a b w :从 a 到 b 的路径上的点的权值都减去 w
Q a : 询问点 a 的权值是多少
这是一个树链剖分的裸题~笨宝宝wa(MLE,TLE,RE)了好多好多次~~~
树链剖分参考博客 呜呜呜~这篇博客非常棒~尤其是他的例子的那个图
莫名其妙的超空间
莫名其妙的超时
re是因为数组开小了~
附代码:
FZOJ2082
鉴于自己的记性实在太差~~~趁现在还会一点点,还是写点什么吧~
对于裸树链剖分关键部分在于把树的所有节点映射到线段树的节点上。我习惯用两次dfs确定顺序
上面的能得到树中各点的深度,重儿子,父亲节点
这个是为了获得各个节点在线段树中的位置(顺带把权值映射到线段树节点上),还有就是top数组了,top[i]表示与i在同一条重链上的深度最小的点,也就是最靠近树根且在同一条重链上
由这个就可以求LCA(没试过,不敢瞎说,大致就是根据top和深度(确定哪一个)不断的向上跳(刚才瞎想了一下觉得对于特定的数据会超时,比如给的数据使它每次只能跳到父节点,这样就要算他们的深度之和次了(假设不在同一条链)如果深度和很大,,,询问次数很多,,就TLE,这个是有可能的,但是要注意这需要非常非常非常非常大的N才可以,N很大的话其他的估计也不好搞)时间复杂度是log级别的,因为,在树中任意一点到根的路径上,重链数不会超过(logn),所以我们可以在O(logn)的时间复杂度内,将a,b的LCA求出来。)
然后就是更新了
~end~
HDU 3966
题意:
给了一个树和各个点的权值,不超过五万个点,有以下三种操作,操作数不超过十万
I a b w :(I是字符"I",a,b,w是数字)表示从 a 到 b 的路径上的点的权值都加上 w
D a b w :从 a 到 b 的路径上的点的权值都减去 w
Q a : 询问点 a 的权值是多少
这是一个树链剖分的裸题~笨宝宝wa(MLE,TLE,RE)了好多好多次~~~
树链剖分参考博客 呜呜呜~这篇博客非常棒~尤其是他的例子的那个图
莫名其妙的超空间
莫名其妙的超时
re是因为数组开小了~
附代码:
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <cstring> #include <algorithm> using namespace std ; #define LL long long #define rep(i,n) for (LL i = 1 ; i <= n ; ++ i) #define intmid LL mid = (A[i].le + A[i].ri)>>1 ; #define lson (i<<1) #define rson (i<<1|1) const LL maxn = 100010 ; LL N , QAQ , h[maxn] , cnt , id , fa[maxn] , siz[maxn] , son[maxn] , dep[maxn] , pos[maxn] , top[maxn] ; LL val[maxn] ; struct EDGE { LL u , to , nxt ; LL w ; }G[maxn]; void Init() { memset(son,-1,sizeof(son)) ; memset(h,-1,sizeof(h)) ; fa[1] = cnt = id = 0 ; } inline void addedge(LL u,LL v,LL w) { G[cnt].u = u , G[cnt].to = v ; G[cnt].nxt = h[u] , G[cnt].w = w ; h[u] = cnt ++ ; G[cnt].u = v , G[cnt].to = u ; G[cnt].nxt = h[v] , G[cnt].w = w ; h[v] = cnt ++ ; } void dfsa(LL rt,LL d) { siz[rt] = 1 , dep[rt] = d ; LL v ; for (LL i = h[rt] ; ~i ; i = G[i].nxt) { v = G[i].to ; if (v != fa[rt]) { fa[v] = rt ; dfsa(v,d+1) ; siz[rt] += siz[v] ; if (son[rt] == -1 || siz[v] > siz[son[rt]]) son[rt] = v ; } } } void dfsb(LL rt,LL tp) { top[rt] = tp , pos[rt] = ++ id ; if (son[rt] == -1) return ; dfsb(son[rt],tp) ; LL v ; for (LL i = h[rt] ; ~i ; i = G[i].nxt) { v = G[i].to ; if (v == fa[rt]) continue ; if (v != son[rt]) dfsb(v,v) ; val[pos[v]] = G[i].w ; } } struct segmenttree { LL le , ri ; LL sc ; }A[maxn<<1]; inline void pushUp(LL i) { A[i].sc = A[lson].sc + A[rson].sc ; } void Build(LL i,LL le,LL ri) { A[i].le = le , A[i].ri = ri , A[i].sc = 0 ; if (le == ri) { A[i].sc = val[le] ; return ; } intmid ; Build(lson,le,mid) ; Build(rson,mid+1,ri) ; pushUp(i) ; } void update(LL i,LL p,LL x) { if (A[i].le == p && A[i].ri == p) { A[i].sc = x ; return ; } intmid ; if (p <= mid) update(lson,p,x) ; else update(rson,p,x) ; pushUp(i) ; } LL Query(LL i,LL le,LL ri) { if (A[i].le == le && A[i].ri == ri) return A[i].sc ; intmid ; if (ri <= mid) return Query(lson,le,ri) ; else if (le > mid) return Query(rson,le,ri) ; else return (Query(lson,le,mid) + Query(rson,mid+1,ri)) ; } inline void solveUpdate(LL i,LL x) { LL u = G[2*i-2].u , v = G[2*i-2].to ; if (dep[u] < dep[v]) swap(u,v) ; update(1,pos[u],x) ; } inline LL solveQuery(LL u,LL v) { LL ans = 0 ; while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) swap(u,v) ; ans += Query(1,pos[top[u]],pos[u]) ; u = fa[top[u]] ; } if (u != v) { if (dep[u] > dep[v]) swap(u,v) ; ans += Query(1,pos[son[u]],pos[v]) ; } return ans ; } int main() { // freopen("in.txt","r",stdin) ; LL u , v ; LL w ; while (scanf("%I64d%I64d",&N,&QAQ) == 2) { Init() ; rep(i,N-1) { scanf("%I64d%I64d%I64d",&u,&v,&w) ; addedge(u,v,w) ; } dfsa(1,1) ; dfsb(1,1) ; Build(1,1,N) ; while (QAQ --) { scanf("%I64d",&u) ; if (u == 0) { scanf("%I64d%I64d",&u,&w) ; solveUpdate(u,w) ; } else if (u == 1) { scanf("%I64d%I64d",&u,&v) ; printf("%I64d\n",solveQuery(u,v)) ; } } } return 0 ; }
FZOJ2082
鉴于自己的记性实在太差~~~趁现在还会一点点,还是写点什么吧~
对于裸树链剖分关键部分在于把树的所有节点映射到线段树的节点上。我习惯用两次dfs确定顺序
void dfsa(int rt,int d) { dep[rt] = d , siz[rt] = 1 ; int v ; for (int i = h[rt] ; ~i ; i = G[i].nxt) { v = G[i].to ; if (v != fa[rt]) { fa[v] = rt ; dfsa(v,d+1) ; if (son[rt] == -1 || siz[v] > siz[son[rt]]) son[rt] = v ; } } }
上面的能得到树中各点的深度,重儿子,父亲节点
void dfsb(int rt,int tp) { top[rt] = tp , pos[rt] = ++ id ; if (son[rt] == -1) return ; dfsb(son[rt],tp) ; int v ; for (int i = h[rt] ; ~i ; i = G[i].nxt) { v = G[i].to ; if (v == son[rt]) val[pos[v]] = G[i].w ; if (v != son[rt] && v != fa[rt]) dfsb(v,v) ; val[pos[v]] = G[i].w ; } }
这个是为了获得各个节点在线段树中的位置(顺带把权值映射到线段树节点上),还有就是top数组了,top[i]表示与i在同一条重链上的深度最小的点,也就是最靠近树根且在同一条重链上
由这个就可以求LCA(没试过,不敢瞎说,大致就是根据top和深度(确定哪一个)不断的向上跳(刚才瞎想了一下觉得对于特定的数据会超时,比如给的数据使它每次只能跳到父节点,这样就要算他们的深度之和次了(假设不在同一条链)如果深度和很大,,,询问次数很多,,就TLE,这个是有可能的,但是要注意这需要非常非常非常非常大的N才可以,N很大的话其他的估计也不好搞)时间复杂度是log级别的,因为,在树中任意一点到根的路径上,重链数不会超过(logn),所以我们可以在O(logn)的时间复杂度内,将a,b的LCA求出来。)
然后就是更新了
inline void solveNgUpdate(int u,int v) { while (top[u] != top[v]) { if (dep[top[u]] < dep[top[v]]) swap(u,v) ; ngupdate(1,pos[top[u]],pos[u]) ; u = fa[top[u]] ; } if (u != v) { if (dep[u] > dep[v]) swap(u,v) ; ngupdate(1,pos[son[u]],pos[v]) ; } }
~end~
相关文章推荐
- C++实验3
- 我的C++第三次上机作业
- 成都Uber优步司机奖励政策(4月10日)
- C++中volatile关键字
- JNI 开发笔记 - 基本概念
- Hadoop安装与配置
- 深入理解 Android 的 IPC 机制--------Binder
- Caffe FastRCNN installing (recommand reference)
- MySQL数据库锁机制之MyISAM引擎表锁和InnoDB行锁详解
- 看科比特如何成为工业级无人机新标杆
- UITabBar初谈
- java 实现二分归并排序
- 随机生成十个数,排顺序
- iOS手势识别
- 【转帖】开源的服务发现
- asp.net 验证码(一)Session
- 资源分享
- 02 VoLTE - Domain Selection 和 VoLTE Signaling Overview
- 直接插入排序
- c++作业 4月10号