LOJ 139 樹鏈剖分

2022-03-29 20:05:19 字數 3466 閱讀 5562

loj#139. 樹鏈剖分

題目描述

這是一道模板題。

給定一棵$n$個節點的樹,初始時該樹的根為 1號節點,每個節點有乙個給定的權值。下面依次進行 m個操作,操作分為如下五種型別:

輸入格式

第一行為乙個整數 n,表示節點的個數。

第二行 n個整數表示第 i個節點的初始權值 $a_i$​​。

第三行 $n-1$ 個整數,表示 $i+1$號節點的父節點編號$f_(1\leqslant f_\leqslant n)$。

第四行乙個整數$m$,表示操作個數。

接下來 $m$行,每行第乙個整數表示操作型別編號:$(1 \leqslant u, v \leqslant n)$

輸出格式

對於每乙個型別為 $4$ 或 $5$ 的操作,輸出一行乙個整數表示答案。

樣例樣例輸入

6

1 2 3 4 5 6

1 2 1 4 4

64 5 6

2 2 4 1

5 11 4

3 1 2

4 2 5

樣例輸出

15

2419

資料範圍與提示

對於 100%的資料,$1\leqslant n,m,k,a_i\leqslant 10^5$​​。資料有一定梯度。

題解here!

這個題如果沒有換根就是一道沙茶題。。。

如果是沙茶題我還會寫部落格嗎。。。

看到換根,想起了$lct$,但是這個$lct$還要維護子樹資訊,簡直毒瘤啊。。。

所以我們選擇樹鏈剖分+線段樹。

線段樹是區間修改,區間加的利器!

當然你真的要用$lct$我也不攔著。。。

沒有換根很好做對吧。

考慮換根對每個操作的影響。

手玩幾個小資料之後發現,如果我們以$1$為根建樹,換根對$2,4$操作是沒有影響的!

所以直接套上板子。

那子樹怎麼辦?

我們不是以$1$為根建樹了嗎?

那就分類討論一下$root$是否在子樹內就好。

1. 如果$x==root$,那就是修改/查詢整棵樹,直接$update(1,n),query(1,n)$就好。

2. 如果$lca(x,root)!=x$,即$id[x]>id[root]\ or\ id[root]>id[x]+size[x]-1$,那麼修改/查詢跟$root$沒有關係,直接修改/查詢$x$的子樹$[id[x],id[x]+size[x]-1]$就好。

3. 如果$lca(x,root)==x$,即$id[x]<=id[root]\ and\ id[root]<=id[x]+size[x]-1$,這時比較麻煩。

我們已經知道$deep[root]>deep[x]$了。

那麼我們發現,換完根後,所改變的只有$x$的所有子樹中,包含$root$的那顆子樹!

那麼我們可以找到$x$的所有子樹中,包含$root$的那顆子樹的根節點,也就是$x$的某個兒子,設為$y$。

然後對除了$y$的子樹$[id[y],id[y]+size[y]-1]$之外的所有節點進行修改/查詢。

也就是對$[1,id[y]-1],[id[y]+size[y],n]$這兩個區間進行修改/查詢。

那怎麼找$y$呢?

我們可以從$root$開始暴力跳鏈,直到重鏈的頂端的父親是$x$。

如果重鏈的頂端的深度大於了$x$的深度,那麼說明$x$與重鏈的頂端在一條重鏈上,直接返回$x$的重兒子$son[x]$即可。

於是這個問題就完美解決了!

媽媽再也不用擔心樹剖做不了換根了!

$update$:原來的板子有錯。。。

但是我很好奇它是怎麼過的。。。

於是修改了一下。

但是原來的$666ms$的提交沒有了555.。。

附**:

#include#include#include#define lson rt<<1

#define rson rt<<1|1

#define data(x) b[x].data

#define sign(x) b[x].c

#define lside(x) b[x].l

#define rside(x) b[x].r

#define width(x) (rside(x)-lside(x)+1)

#define maxn 100010

using namespace std;

int n,m,c=1,d=1,root;

int val[maxn],head[maxn],deep[maxn],son[maxn],size[maxn],fa[maxn],id[maxn],pos[maxn],top[maxn];

struct treea[maxn<<1];

struct segment_treeb[maxn<<2];

inline int read()

while(c>='0'&&c<='9')

return date*w;

}inline void pushup(int rt)

inline void pushdown(int rt)

void buildtree(int l,int r,int rt)

int mid=l+r>>1;

buildtree(l,mid,lson);

buildtree(mid+1,r,rson);

pushup(rt);

}void update(int l,int r,long long c,int rt)

pushdown(rt);

int mid=lside(rt)+rside(rt)>>1;

if(l<=mid)update(l,r,c,lson);

if(mid>1;

if(l<=mid)ans+=query(l,r,lson);

if(midsize[son[rt]])son[rt]=will;}}

}void dfs2(int rt,int f)

}int check(int x)

return son[x];

}return 0;

}void update_path(int x,int y,int k)

void update_subtree(int x,int k)

}void query_path(int x,int y)

void query_subtree(int x)

void work()

case 3:

case 4:

case 5:query_subtree(x);break;}}

}void init()

m=read();

root=1;

deep[1]=1;

dfs1(1);

dfs2(1,1);

buildtree(1,n,1);

}int main()

樹鏈剖分 樹鏈剖分講解

好了,這樣我們就成功解決了對樹上修改查詢邊權或點的問題。下面放上 vector v maxn int size maxn dep maxn val maxn id maxn hson maxn top maxn fa maxn 定義 int edge 1,num 1 struct tree e ma...

演算法入門 樹鏈剖分 輕重鏈剖分

目錄 3.0 求 lca 4.0 利用資料結構維護資訊 5.0 例題 參考資料 資料結構入門 線段樹 發表於 2019 11 28 20 39 dfkuaid 摘要 線段樹的基本 建樹 區間查詢 單點修改 及高階操作 區間修改 單點查詢 區間修改 區間查詢 標記下傳 標記永久化 閱讀全文 樹鏈剖分用...

樹鏈剖分 樹剖換根

這是一道模板題。給定一棵 n 個節點的樹,初始時該樹的根為 1 號節點,每個節點有乙個給定的權值。下面依次進行 m 個操作,操作分為如下五種型別 換根 將乙個指定的節點設定為樹的新根。修改路徑權值 給定兩個節點,將這兩個節點間路徑上的所有節點權值 含這兩個節點 增加乙個給定的值。修改子樹權值 給定乙...