Virtual Tree 學習筆記

2022-06-29 23:51:13 字數 2597 閱讀 5599

像大部分虛樹介紹一樣,以一道 爛大街 的例題 [sdoi2011]消耗戰 引入:

給定一棵大小為 \(n\) 的樹,每條邊有邊權,\(m\) 組詢問,每次詢問給定 \(k\) 個關鍵點,要求切斷 \(1\) 與所有關鍵點的路徑,求最小代價。

\(2 \leq n \leq 2.5 \times 10^5\),\(m \geq 1\),\(\sum \leq 5 \times 10^5\),\(1 \leq k \leq n - 1\)

設 \(f_u\) 表示切斷 \(u\) 與其子樹中所有關鍵點的最小代價。

設 \(w_\) 表示邊 \((u, v)\) 的權值。

列舉 \(u\) 的兒子 \(v\),轉移分為兩類:

樸素做法的時間複雜度為 \(o(nq)\),並過不掉這題,所以我們需要進行優化。

可以注意到,我們浪費的很多時間 \(dp\)非關鍵點,並且關鍵點的總數是與 \(n\) 同階的,所以考慮能否濃縮資訊,大樹變小

這時引出虛樹的概念。

直觀的感受一下:

選取不同關鍵點,建出來的虛樹如下所示:

(以上圖** oi wiki)

任意兩個關鍵點的 \(lca\) 也需要儲存重要資訊,我們需要將其保留,所以虛樹中不一定只包含關鍵點

以及我們可以發現,虛樹中祖先後代關係並不會改變。

接下來直接搬運一波建樹方法:

首先很直觀的,可以將所有關鍵點按 \(dfs\) 序排序,遍歷一遍,兩兩間求求 \(lca\),判判重,連連邊,就建完啦!(逃)

樸素演算法複雜度較高,考慮單調棧,單調棧的作用是:維護虛樹上的一條鏈

為了方便,首先將根節點加入棧中,然後按 \(dfs\) 序遍歷關鍵點:

若當前點 \(u\) 與棧頂節點的 \(lca\) 是棧頂節點,那麼說明 \(u\) 與棧中節點在一條鏈上,直接將 \(u\) 壓入棧中

若 \(lca\) 不是棧頂節點,

需注意一些細節:

退棧時,一般棧頂節點在虛樹中的父親為第二節點,但是最後彈出的棧頂節點的父親為 \(lca\) (注意我們最後把 \(lca\) 加入棧中了);

如例題,我們需要多次建虛樹,則每次需要清空存圖的陣列,一般在節點入棧時清空即可(如鄰接表,前向行 \(head\) 陣列等);

如例題,每一條虛樹中的邊(或點)可能濃縮的原樹中幾條邊的資訊,所以我們有時需要先求出濃縮資訊,然後在連邊賦上。例題中我用了倍增的方法在連邊時 \(logn\) 求出一條虛樹上邊的權值(其實就是幾條原樹邊取個 \(min\))。

對於例題,建出虛樹後,直接在上面跑樸素 \(dp\) 就可以啦!

\(code\)

#include #include #include using namespace std;

#define n 1000000

#define l 19

#define fo(i, u) for(int i = head[u]; i; i = edge[i].next)

#define fo(i, x, y) for(int i = x, end_##i = y; i <= end_##i; ++ i)

#define fd(i, x, y) for(int i = x; i >= y; i --)

typedef long long ll;

void read(int &x)

struct edge edge[n << 1];

int head[n + 1], pre[n + 1][l + 1], g[n + 1][l + 1], sta[n + 1], dfn[n + 1], a[n << 1], dep[n + 1], key[n + 1];

ll f[n + 1];

int n, m, len, now = 0;

int cnt_edge = 1;

void add(int u, int v, int w) , head[u] = cnt_edge; }

void link(int u, int v, int w)

void input()

int tot_dfn = 0;

void dfs1(int u, int la)

void init()

bool cmp(int x, int y)

int get_lca(int u, int v)

int get_dis(int u, int v)

ll min(ll a, ll b)

void build()

sta[ ++ top ] = a[i], head[a[i]] = 0;

}fo(i, 1, top - 1)

add(sta[i], sta[i + 1], get_dis(sta[i + 1], sta[i]));

}void dfs2(int u)

}void solve()

int main()

return 0;

}

\(solution\)

待補充......

虛樹 Virtual Tree 學習筆記

description 在一場戰爭中,戰場由n個島嶼和n 1個橋梁組成,保證每兩個島嶼間有且僅有一條路徑可達。現在,我軍已經偵查到敵軍的總部在編號為1的島嶼,而且他們已經沒有足夠多的能源維繫戰鬥,我軍勝利在望。已知在其他k個島嶼上有豐富能源,為了防止敵軍獲取能源,我軍的任務是炸毀一些橋梁,使得敵軍不...

日常學習 Virtual Tree 虛樹)

虛樹,顧名思義就是虛構的樹,它是一種用來解決樹上問題的演算法,主要思想是只將原樹上必要的點和它們的最近公共祖先取出來,構成一棵虛樹,並保留他們在樹上的相對關係。我們先來看一道題 給定一棵n個點的樹,每次詢問給定乙個大小為k的點集,你需要切掉一些邊,使得點集中的點均不與1號點聯通,而每條邊都有被切掉所...

學習筆記 雜湊學習筆記

hash基本原理 hash就是乙個像函式一樣的東西,你放進去乙個值,它給你輸出來乙個值。輸出的值就是hash值。一般hash值會比原來的值更好儲存 更小 或比較。那字串hash就非常好理解了。就是把字串轉換成乙個整數的函式。而且要盡量做到使字串對應唯一的hash值。它的主要思路是選取恰當的進製,可以...