今天做了一道點分治的題目,所以就去網上學了一下。
相信大家都聽說過「分治」吧,分治就是「分而治之」一般是把n分成2份,然後再對每乙份進行相同的操作,最後合併起來。
而點分治,一般情況下是在一棵樹上面進行分治,和普通的分治大同小異。
先看一道例題
【題意】
給定乙個有n個點(編號1,2,…,n)的樹,每條邊都有乙個權值(不超過1000)。
樹上兩個節點x與y之間的路徑長度就是路徑上各條邊的權值之和。
求長度不超過k的路徑有多少條。
poj 1741 – tree
【輸入格式】
輸入包含多組測試樣例。
每組測試樣例的第一行包含兩個正整數n和k。
接下來n-1行,每行包含三個正整數u,v,l,表示節點u與v之間存在一條邊,且邊的權值為l。
當輸入樣例n=0,k=0時,表示輸入終止,且該樣例無需處理。
【輸出格式】
每個測試樣例輸出乙個結果。
每個結果佔一行。
【資料範圍】
n≤10000,k≤10^7
【輸入樣例】
5 41 2 3
1 3 1
1 4 2
3 5 1
0 0【輸出樣例】
8先是考慮暴力的解法,直接列舉每乙個點,求lca,然後判斷是否<=k,可以得10分
仔細想想,對於乙個點來說,長度<=k的路徑無非就只有兩種:
1.經過該點
2.不經過該點
所以我們採取點分治的做法,先選1作為根節點,求出經過1且<=k的路徑條數,然後刪掉1,再對1的每個兒子進行重複操作
那麼我們怎麼求出路徑的條數呢
設d[i]為i到根節點root的距離,其中d[root]=0
搜尋求出d,對d從小到大排序
然後我們令指標l = 1, r = tp (tp為d中,點的個數)
如果 d[l] + d[r] <= k ,結果就加上r-l+1,然後l++
否則r- -
但是這樣會出現乙個問題,比如son是root的乙個兒子,在son的子樹中,有很多個加起來滿足<=k的,而這些都不經過u,怎麼辦???
容斥原理!!!
對於每個兒子,令它的d不變,統計一次<=k的個數,從ans裡面減去這個數就好了。
這樣可以取得70分
為什麼只有70呢
舉個例子吧,如果樹退化成鏈的話,這個做法就和暴力沒有區別,時間複雜度o(n
2log
n)o(n^2logn)
o(n2lo
gn)怎麼辦呢,因為這是一棵無根樹,每乙個點成為根節點都不會影響結果,所以我們每次都從當前的樹種求出它的重心作為root,這樣的話,無論樹怎麼樣,都只有log
nlogn
logn
層,時間複雜度o(n
這是形象的搜尋過程
參考**
#include
#include
#include
using
namespace std;
const
int n =
10006
;int n, m, k, all, ans, root;
//m是為了好看,all表示當前的樹中節點總數,root表示根節點
int tot, head[n]
, ver[n<<1]
, leng[n<<1]
, next[n<<1]
;//儲存邊
int max_part[n]
, size[n]
, d[n]
;//max_part[i]表示刪去i以後剩餘的最大部分,size表示子樹大小,d表示到根節點的距離
int tp, sta[n]
;//儲存每個點到根節點的距離
bool vis[n]
;//判斷這個點是否做過做過根節點(是否被刪除)
void
add(
int u,
int v,
int w)
void
dfs1
(int u,
int fa)
max_part[u]
=max
(max_part[u]
, all - max_part[u]);
if(max_part[u]
< max_part[root]
) root = u;
}void
dfs2
(int u,
int fa)
}int
calc
(int u,
int now)
void
solve
(int u)
}int
main()
all = n, max_part[0]
= n, root =0;
ans =0;
dfs1(1
,0);
solve
(root)
;printf
("%d\n"
, ans);}
return0;
}
詳解點分治
本篇隨筆講解演算法競賽中的點分治澱粉質演算法。點分治是一種在樹上進行路徑靜態統計的演算法。所謂樹上的靜態統計,並不是像樹剖一樣維護路徑最值 路徑和之類的統計。那樣的話,這個演算法的存在就沒什麼意義了。來上道小題體會一下點分治的解決問題。給定一棵有n個節點的帶邊權無根樹。求長度不超過k的路徑有多少條。...
點分治 模板 詳解
動態點分治學了以後會在後面 update 的啦 好久沒頹 blog 了 今天來寫一發 最近幾個月就學了這乙個東西啊 好了 進入正題 舌尖上的澱粉質 q1 點分治是什麼?a1 就是像分治一樣把樹上的點咔擦成幾個小樹,然後繼續咔擦下去處理問題啦 像你把西蘭花掰開一樣 q2 點分治能用來幹什麼?a2 據我...
點分治 動態點分治
實在拖得太久了。先扔掉資料 分治的核心是盡量把乙個整體分成接近的兩個部分,這樣遞迴處理可以讓複雜度從n 變成nlogn。兩個問題,如何區分和如何算答案。對於第乙個問題,重心,然後就是找重心的方法,兩個dfs,對於第二個問題,對於每個重心算當前塊中每個點到重心的答案,然後由重心分開的塊要把多餘的資訊去...