您的位置:首页 > 其它

树链剖分来一发

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是因为数组开小了~

附代码:

#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~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: