關於構建虛樹和LCA的問題

2021-10-06 10:57:22 字數 2798 閱讀 1304

新學虛樹,在此記錄一下。

首先得搞明白什麼是虛樹

虛樹經常被使用在樹形dp中,往往會給你一顆相對複雜的樹。然後給你一系列的查詢條件,即包含一系列的查詢關鍵點,然後讓你在這些少量的關鍵點中進行查詢遍歷得出你想要的結果。而此時你對整棵樹dp在時間上是不允許的。而這時你就要通過其他途徑僅僅對要查詢的關鍵點進行遍歷,而減去其他的無用的樹節點。這時,虛樹這個概念就可以引入了。我們建立一顆僅僅包含部分關鍵結點的虛樹,將非關鍵點構成的鏈簡化成邊或是剪去,在虛樹上進行dp。

建立虛樹之前的準備工作

如何構建虛樹

我們使用棧stack來維護所謂的最右鏈,top為棧頂位置。在一開始,最右鏈上的邊並沒有被加入虛樹,這是因為在接下來的過程中隨時會有某個lca插到最右鏈中。初始無條件將第乙個詢問點加入棧stack中。然後接下來依次將排序後的節點加入,加入該詢問點為now,此時它們的最近公共祖先lc = lca(now,stack[top]);要考慮stack[top]和stack[top - 1]以及lc之間的關係。

這裡可以分情況討論

1 lc = stack[top],也就是說now在stack[top]的子樹中。此時只需將now入棧即可。

2 lc = stack[top - 1], 直接把lc -> stack[top]這條邊加入虛樹,stack[top]出棧,top-- ,再將now入棧。

3 lc 在stack[top]和stack[top-1]之間,也是先把lc -> stack[top]這台邊加入虛樹,stack[top]出棧,top–,

再將lc和now都入棧。與情況二有點類似。

4 dep[lc] < dep[stack[top - 1]],我們需要迴圈依次將stack[top - 1] -> stack[top]這條邊依次加入虛樹,stack[top]出棧,top–。直到出現情況二為止。

以上四種情況自己手動畫下,如若實在不懂,可以去其他大佬的部落格進行食用,他們的更加通俗易懂。

下面引入乙個例子。

p2495 [sdoi2011]消耗戰

一些問題

對於每次詢問都要進行清除虛樹構建,因為每一次詢問都要重新構建一顆虛樹。

這裡涉及的最短路徑和,我們預處理出minv[u]代表從1到u節點路徑上最小的邊權。如果u是詢問點,那麼切斷u及其子樹上詢問點的最小代價dp[u] = minv[u],否則,最小代價dp(u)=min(minv[u],sum)其中sum是u節點的子節點的邊權之和。這裡得注意的是,本來你找到了最近的查詢點後,本來不用管子節點此時的邊權是最小的,但是,你還是得查詢這個節點的兒子進行遍歷清除整棵虛樹。

然後直接上**。

#include

#include

#include

#include

using namespace std;

const int maxn = 500050;

int head[maxn],n,m,tol,dep[maxn],m[maxn],tol1;

int list[maxn],flag[maxn],stack[maxn],head1[maxn]

;int fa[25]

[maxn],dfn[maxn],dfncnt = 1;

long long minv[maxn]

;struct edgee[maxn << 1],e1[maxn << 1]

;bool cmp(int u, int v)

void add(int u, int v, long long w)

void dfs(int u, int f, int d)

m[u]

= k; //所能走到的最大k的位置

for(int i = head[u]

; i; i = e[i].next)}}

int lca(int u,int v)

} if(u == v)

return u;

for(int i = m[u]

; i >= 0; i --)

}return fa[0]

[u];

}long long dfs1(int u)

//如果這個節點是標記點 即為查詢目標點

if(flag[u]

== 1)

min = minv[u]

;else

min = min(minv[u],sum)

; //清除虛樹

flag[u]

= 0;

head1[u]

= 0;

return min;

} void add1(int u,int v)

int main(

) dfs(1,0,1)

; cin >> m;

for(int i = 1; i <= m; i ++)

//對list中的每個節點根據時間序排序

sort(list + 1, list + k + 1,cmp)

;

int top

= 1;

stack[top]

= list[1]

; for(int j = 2; j <= k; j ++)

break

;}else

} stack[++top]

= now;

} //將最右鏈放入虛樹

while(--top)

//計算結果

cout << dfs1(stack[1]

)<< endl;

//重新構建虛樹初始化

tol1 = 0;

}return 0;

}

樹的構建,遍歷和判斷

include using namespace std include include include typedef struct node bitree,bitreenode 1.先序和中序建立樹 bitree preinorder char preord,char inord,int i,in...

關於二叉樹的構建

二叉樹的前序 中序和後序序列中的任何乙個都不能唯一確定一棵二叉樹。二叉樹的構建主要有兩大種方法 第一種是根據前序 中序或者後序 中序來唯一確定二叉樹的結構 這個課本上都有講 第二種是根據二叉樹對應的擴充二叉樹的先序或者後序序列來確定。本文主要討論部分 上面說的第二種,有些文件資料甚至課本裡說根據擴充...

關於虛函式的預設實參的問題

a.h class a a.cpp void a log int a,int b,int c const std endl b.h include a.h class b public a b.cpp void b log int a,int b,int c const std endl main....