樹上差分算法解析與邊操作優(yōu)化實(shí)踐
1. 項(xiàng)目概述樹上差分與邊差分算法解析這道題目來自AcWing在線編程平臺的4963題核心考察的是如何高效處理樹結(jié)構(gòu)上的邊操作問題。題目要求我們在給定的一棵樹上通過一系列操作后確定可以安全移除的邊。這類問題在實(shí)際應(yīng)用中非常常見比如網(wǎng)絡(luò)路由優(yōu)化、社交網(wǎng)絡(luò)關(guān)系分析等領(lǐng)域都會遇到類似場景。1.1 問題核心需求題目給出一個具有N個節(jié)點(diǎn)的樹結(jié)構(gòu)以及M個操作請求。每個操作指定兩個節(jié)點(diǎn)u和v表示需要在這兩個節(jié)點(diǎn)之間的唯一路徑上的所有邊都執(zhí)行某種操作通常是增加或減少某個值。最終我們需要找出那些被所有操作覆蓋的邊或者說滿足特定條件的邊。這類問題的難點(diǎn)在于樹結(jié)構(gòu)的特殊性導(dǎo)致直接暴力解法時間復(fù)雜度太高O(M*N)需要高效處理大量區(qū)間更新操作最終需要精確到邊的統(tǒng)計(jì)結(jié)果1.2 算法選型思路針對這類問題我們通常會考慮以下幾種算法暴力DFS/BFS對每個操作都遍歷整條路徑時間復(fù)雜度不可接受樹鏈剖分雖然可以解決問題但實(shí)現(xiàn)復(fù)雜且常數(shù)較大樹上差分最優(yōu)選擇可以將時間復(fù)雜度降到O(M N)樹上差分算法之所以成為最優(yōu)解是因?yàn)轭A(yù)處理階段只需要O(N)時間每個操作可以在O(1)時間內(nèi)完成最終通過一次DFS遍歷就能得到所有邊的最終狀態(tài)2. 核心算法原理詳解2.1 差分?jǐn)?shù)組基礎(chǔ)概念在講解樹上差分之前我們先回顧一下一維差分?jǐn)?shù)組的概念。差分是一種常用的區(qū)間更新技巧它允許我們在O(1)時間內(nèi)完成任意區(qū)間的增減操作。對于普通數(shù)組arr我們定義其差分?jǐn)?shù)組diff滿足diff[0] arr[0]diff[i] arr[i] - arr[i-1] (i 0)這樣如果我們想對arr的區(qū)間[l,r]增加val只需要diff[l] valdiff[r1] - val最后通過前綴和運(yùn)算即可還原出更新后的arr數(shù)組。2.2 樹上差分的擴(kuò)展應(yīng)用將差分思想擴(kuò)展到樹結(jié)構(gòu)上我們需要考慮樹的特殊性質(zhì)樹是連通無向無環(huán)圖任意兩點(diǎn)之間有且只有一條唯一路徑邊和節(jié)點(diǎn)可以分別作為操作對象在本題中我們需要處理的是邊差分區(qū)別于點(diǎn)差分。邊差分的關(guān)鍵在于將每條邊關(guān)聯(lián)到其下方的節(jié)點(diǎn)通過節(jié)點(diǎn)的差分值來反映邊的狀態(tài)具體來說對于邊(u,v)其中u是v的父節(jié)點(diǎn)我們將這條邊的狀態(tài)記錄在v節(jié)點(diǎn)上。這樣整棵樹的邊就與除根節(jié)點(diǎn)外的所有節(jié)點(diǎn)建立了一一對應(yīng)關(guān)系。2.3 LCA最近公共祖先的作用在處理路徑操作時我們需要快速找到任意兩個節(jié)點(diǎn)的最近公共祖先。LCA算法可以幫助我們將路徑拆分為u→LCA和v→LCA兩部分在這兩部分上分別應(yīng)用差分操作常用的LCA算法有樸素算法O(n)查詢倍增法O(logn)查詢需要預(yù)處理Tarjan離線算法O(1)查詢但需要預(yù)處理在本題中我們通常選擇倍增法因?yàn)轭A(yù)處理時間O(nlogn)可以接受查詢速度快適合處理大量操作實(shí)現(xiàn)相對簡單3. 完整算法實(shí)現(xiàn)步驟3.1 數(shù)據(jù)結(jié)構(gòu)預(yù)處理首先我們需要建立樹的基本數(shù)據(jù)結(jié)構(gòu)并進(jìn)行必要的預(yù)處理const int MAXN 1e5 10; const int LOGN 20; vectorint tree[MAXN]; // 鄰接表存儲樹結(jié)構(gòu) int depth[MAXN]; // 節(jié)點(diǎn)深度 int parent[MAXN][LOGN]; // 倍增表 int diff[MAXN]; // 差分?jǐn)?shù)組 int edge_id[MAXN]; // 記錄邊與節(jié)點(diǎn)的對應(yīng)關(guān)系3.2 DFS預(yù)處理實(shí)現(xiàn)我們需要進(jìn)行一次DFS遍歷來完成以下工作計(jì)算每個節(jié)點(diǎn)的深度構(gòu)建倍增表建立邊與節(jié)點(diǎn)的對應(yīng)關(guān)系void dfs(int u, int p) { parent[u][0] p; depth[u] depth[p] 1; // 構(gòu)建倍增表 for(int i 1; i LOGN; i) { parent[u][i] parent[parent[u][i-1]][i-1]; } // 遍歷子節(jié)點(diǎn) for(int v : tree[u]) { if(v ! p) { edge_id[v] /* 記錄邊(u,v)的id */; dfs(v, u); } } }3.3 LCA查詢實(shí)現(xiàn)基于預(yù)處理好的倍增表我們可以高效查詢?nèi)我鈨牲c(diǎn)的LCAint lca(int u, int v) { if(depth[u] depth[v]) swap(u, v); // 將u提升到與v同一深度 for(int i LOGN-1; i 0; i--) { if(depth[parent[u][i]] depth[v]) { u parent[u][i]; } } if(u v) return u; // 同時向上尋找 for(int i LOGN-1; i 0; i--) { if(parent[u][i] ! parent[v][i]) { u parent[u][i]; v parent[v][i]; } } return parent[u][0]; }3.4 樹上差分操作實(shí)現(xiàn)對于每個操作(u, v)我們這樣處理void apply_diff(int u, int v, int val) { int ancestor lca(u, v); diff[u] val; diff[v] val; diff[ancestor] - 2 * val; }這個操作的核心思想是將路徑拆分為u→ancestor和v→ancestor兩部分在u和v處增加val表示從這兩個節(jié)點(diǎn)到根節(jié)點(diǎn)的路徑都增加val在ancestor處減去2*val抵消掉重復(fù)計(jì)算的部分3.5 結(jié)果收集與邊統(tǒng)計(jì)最后我們通過一次DFS遍歷來收集結(jié)果int result[MAXN]; // 存儲每條邊的最終值 void collect_result(int u, int p) { for(int v : tree[u]) { if(v ! p) { collect_result(v, u); result[edge_id[v]] diff[v]; diff[u] diff[v]; // 向上傳遞差分值 } } }4. 算法優(yōu)化與注意事項(xiàng)4.1 時間復(fù)雜度分析讓我們分析一下算法的時間復(fù)雜度DFS預(yù)處理O(NlogN)主要來自倍增表構(gòu)建M次操作處理每次O(1)差分操作 O(logN)的LCA查詢 → O(MlogN)結(jié)果收集O(N)總時間復(fù)雜度為O((NM)logN)這在N和M達(dá)到1e5量級時是完全可行的。4.2 常見實(shí)現(xiàn)陷阱在實(shí)際編碼中有幾個容易出錯的地方需要注意根節(jié)點(diǎn)的選擇理論上可以選擇任意節(jié)點(diǎn)作為根但通常選擇節(jié)點(diǎn)1作為根更方便需要確保DFS預(yù)處理時正確處理根節(jié)點(diǎn)的parent和depth邊的編號處理需要建立邊與節(jié)點(diǎn)的明確對應(yīng)關(guān)系可以使用map或額外數(shù)組來記錄特別注意無向邊的雙向處理差分值的傳遞在collect_result中需要先處理子節(jié)點(diǎn)再累加差分值順序錯誤會導(dǎo)致結(jié)果不正確邊界條件處理當(dāng)u或v就是LCA時的特殊情況根節(jié)點(diǎn)的特殊處理4.3 調(diào)試技巧當(dāng)算法出現(xiàn)問題時可以采用以下調(diào)試方法小數(shù)據(jù)測試構(gòu)造簡單的樹結(jié)構(gòu)如鏈狀、星狀手動計(jì)算預(yù)期結(jié)果與程序輸出對比差分值打印在每個操作后打印關(guān)鍵節(jié)點(diǎn)的差分值驗(yàn)證差分操作是否正確LCA驗(yàn)證隨機(jī)選擇節(jié)點(diǎn)對驗(yàn)證LCA計(jì)算是否正確可以先用樸素算法驗(yàn)證結(jié)果可視化將最終結(jié)果標(biāo)記在樹的邊上直觀檢查是否符合預(yù)期5. 完整代碼框架示例以下是整合了所有步驟的完整代碼框架#include iostream #include vector #include algorithm using namespace std; const int MAXN 1e5 10; const int LOGN 20; vectorint tree[MAXN]; int depth[MAXN], parent[MAXN][LOGN]; int diff[MAXN], edge_id[MAXN], result[MAXN]; void dfs(int u, int p) { parent[u][0] p; for(int i 1; i LOGN; i) { parent[u][i] parent[parent[u][i-1]][i-1]; } for(int v : tree[u]) { if(v ! p) { depth[v] depth[u] 1; edge_id[v] /* 設(shè)置邊id */; dfs(v, u); } } } int lca(int u, int v) { if(depth[u] depth[v]) swap(u, v); for(int i LOGN-1; i 0; i--) { if(depth[parent[u][i]] depth[v]) { u parent[u][i]; } } if(u v) return u; for(int i LOGN-1; i 0; i--) { if(parent[u][i] ! parent[v][i]) { u parent[u][i]; v parent[v][i]; } } return parent[u][0]; } void apply_diff(int u, int v, int val) { int a lca(u, v); diff[u] val; diff[v] val; diff[a] - 2 * val; } void collect_result(int u, int p) { for(int v : tree[u]) { if(v ! p) { collect_result(v, u); result[edge_id[v]] diff[v]; diff[u] diff[v]; } } } int main() { int N, M; cin N M; // 建樹 for(int i 1; i N; i) { int u, v; cin u v; tree[u].push_back(v); tree[v].push_back(u); } // 預(yù)處理 depth[1] 1; dfs(1, 0); // 處理操作 while(M--) { int u, v; cin u v; apply_diff(u, v, 1); } // 收集結(jié)果 collect_result(1, 0); // 輸出滿足條件的邊 for(int i 1; i N; i) { if(result[i] M) { // 根據(jù)題目條件調(diào)整 cout i ; } } return 0; }6. 算法擴(kuò)展與應(yīng)用樹上差分算法不僅適用于這道題目還可以解決許多類似的樹結(jié)構(gòu)問題點(diǎn)差分當(dāng)操作對象是節(jié)點(diǎn)而非邊時差分公式變?yōu)閐iff[u] val, diff[v] valdiff[lca] - val, diff[parent[lca]] - val帶權(quán)操作每個操作可以有不同的權(quán)值只需將固定的1改為變量即可多條件查詢不只是統(tǒng)計(jì)覆蓋次數(shù)可以統(tǒng)計(jì)總和、最大值、最小值等動態(tài)樹結(jié)構(gòu)結(jié)合LCT等數(shù)據(jù)結(jié)構(gòu)可以處理動態(tài)變化的樹結(jié)構(gòu)在實(shí)際工程應(yīng)用中這種算法思想可以用于網(wǎng)絡(luò)流量監(jiān)控社交網(wǎng)絡(luò)影響分析分布式系統(tǒng)狀態(tài)同步版本控制系統(tǒng)變更追蹤理解了這個核心算法后可以解決LeetCode、Codeforces等平臺上的許多樹結(jié)構(gòu)問題如路徑求和問題子樹統(tǒng)計(jì)問題樹結(jié)構(gòu)區(qū)間更新問題掌握樹上差分的關(guān)鍵在于理解差分思想如何從線性結(jié)構(gòu)擴(kuò)展到樹結(jié)構(gòu)以及如何利用LCA來分解路徑操作。通過這道題目的練習(xí)可以建立起處理復(fù)雜樹結(jié)構(gòu)問題的通用思維框架。

相關(guān)新聞

GPT與Claude雙模型智能融合:解決AI開發(fā)中的模型選擇難題

GPT與Claude雙模型智能融合:解決AI開發(fā)中的模型選擇難題

這次我們來看一個讓工程師們不再需要在大模型之間二選一的解決方案——GPT 5.6 Sol 和 Claude Fable 5 的直接融合技術(shù)。這個項(xiàng)目不是簡單的模型切換,而是通過智能融合機(jī)制讓兩個頂級模型協(xié)同工作,解決單一模型在某些場景下的局限性。從技術(shù)角度看&#…

2026/8/1 3:19:43 閱讀更多
公寓管理軟件對比:全房通、好房通、悅居通,入住交割怎么選?

公寓管理軟件對比:全房通、好房通、悅居通,入住交割怎么選?

連鎖中介開始經(jīng)營公寓直營業(yè)務(wù)后,入住交割往往是最能檢驗(yàn)系統(tǒng)能力的環(huán)節(jié)。它不只是簽完合同后把鑰匙交給租客,而是要同步確認(rèn)房屋狀態(tài)、家具家電、門鎖權(quán)限、水電底數(shù)、費(fèi)用賬單、服務(wù)責(zé)任和門店業(yè)績。公寓管理軟件如果只覆蓋合同或成交,交割…

2026/8/1 12:50:41 閱讀更多
OpenSSL EVP對稱加密接口詳解:從算法抽象到AEAD實(shí)戰(zhàn)

OpenSSL EVP對稱加密接口詳解:從算法抽象到AEAD實(shí)戰(zhàn)

1. 從“裸奔”到“標(biāo)準(zhǔn)接口”:為什么我們需要EVP系列函數(shù)如果你在C/C里搞過對稱加解密,大概率是從AES_encrypt、AES_decrypt這類直接調(diào)用底層算法的函數(shù)開始的。代碼寫起來挺直接,但很快就發(fā)現(xiàn)不對勁:你得自己處理分組模式&#x…

2026/8/1 12:50:41 閱讀更多
SEO實(shí)戰(zhàn):長尾關(guān)鍵詞與內(nèi)容優(yōu)化提升流量

SEO實(shí)戰(zhàn):長尾關(guān)鍵詞與內(nèi)容優(yōu)化提升流量

1. 為什么SEO仍然是流量增長的核心引擎 在信息爆炸的時代,網(wǎng)站流量獲取成本越來越高。我運(yùn)營過十幾個不同行業(yè)的網(wǎng)站,發(fā)現(xiàn)那些依賴付費(fèi)廣告的站點(diǎn)一旦停止投放,流量就會斷崖式下跌。而那些持續(xù)做好SEO的網(wǎng)站,即使三年不更新廣告&a…

2026/8/1 12:50:41 閱讀更多
2026年商用工業(yè)通用UL變壓器貨源廠家盤點(diǎn)

2026年商用工業(yè)通用UL變壓器貨源廠家盤點(diǎn)

在全球商用與工業(yè)電氣系統(tǒng)中,UL認(rèn)證變壓器早已成為項(xiàng)目合規(guī)落地的核心門檻。尤其在出口北美市場、高端智能制造、數(shù)據(jù)中心、新能源配套等場景,一臺穩(wěn)定可靠的UL變壓器,直接關(guān)系到整線設(shè)備的安全認(rèn)證與長期運(yùn)行效率。2026年,隨著供…

2026/8/1 12:40:41 閱讀更多
AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O分配PCB板是應(yīng)用材料(Applied Materials)公司生產(chǎn)的一款用于半導(dǎo)體設(shè)備的I/O信號分配電路板。該型號(0100-02186)的核心特點(diǎn)如下:專用于Endura等半導(dǎo)體工藝腔室。集成信號路由與分配功能。連接控制…

2026/8/1 0:09:33 閱讀更多
Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)是日本日清(Nissei)品牌的一款工業(yè)用三相異步電機(jī),適用于自動化設(shè)備及通用機(jī)械驅(qū)動。該型號(FFMN-32L-10-T0 40AX)的核心特點(diǎn)如下:三相交流異步電動機(jī)。額定…

2026/8/1 0:09:33 閱讀更多
AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O分配PCB板是應(yīng)用材料(Applied Materials)公司生產(chǎn)的一款用于半導(dǎo)體設(shè)備的I/O信號分配電路板。該型號(0100-02186)的核心特點(diǎn)如下:專用于Endura等半導(dǎo)體工藝腔室。集成信號路由與分配功能。連接控制…

2026/8/1 0:09:33 閱讀更多
Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機(jī)是日本日清(Nissei)品牌的一款工業(yè)用三相異步電機(jī),適用于自動化設(shè)備及通用機(jī)械驅(qū)動。該型號(FFMN-32L-10-T0 40AX)的核心特點(diǎn)如下:三相交流異步電動機(jī)。額定…

2026/8/1 0:09:33 閱讀更多