樹的直徑是指樹上的最長簡單路。
任選一點w
為起點,對樹進行搜尋,找出離w
最遠的點u
。
以u
為起點,再進行搜尋,找出離u
最遠的點v
。則u
到v
的路徑長度即為樹的直徑。
簡單證明:
如果w
在直徑上,那麼u
一定是直徑的乙個端點。反證:若u
不是端點,設直徑的兩端為s
與t
,則dist(w, u) > dist(u, t)
且dist(w, u) > dist(u, s)
,則最長路不是s - t
了,與假設矛盾。
如果w
不在直徑上,且w
到其距最遠點u
的路徑與直徑一定有一交點c
,那麼由上乙個證明可知,u
是直徑的乙個端點。
如果w
到最遠點u
的路徑與直徑沒有交點,設直徑的兩端為s
與t
,那麼dist(w, u) > dist(w, c) + dist(c, t)
,推出dist(s, c) + dist(w, u) + dist(w, c) > dist(s, c) + dist(c, t) = dist(s, t)
,最長路不是s - t
與假設矛盾。
因此w
到最遠點u
的路徑與直徑必有交點。
s-----------c-----------t
|w------u
樹的重心 —— by patrickzhou
何謂重心
樹的重心:找到乙個點,以它為整棵樹的根,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心,刪去重心後,生成的多棵樹盡可能平衡。
樹的重心有下面幾條常見性質:樹的重心可以通過簡單的兩次搜尋求出,第一遍搜尋求出以定義1:找到乙個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心。
定義2:以這個點為根,那麼所有的子樹(不算整個樹自身)的大小都不超過整個樹大小的一半。
性質1:樹中所有點到某個點的距離和中,到重心的距離和是最小的;如果有兩個重心,那麼他們的距離和一樣。
性質2:把兩個樹通過一條邊相連得到乙個新的樹,那麼新的樹的重心在連線原來兩個樹的重心的路徑上。
性質3:把乙個樹新增或刪除乙個葉子,那麼它的重心最多隻移動一條邊的距離。
i
為根的每個結點的子結點數量son[i]
,第二遍搜尋找出以 u 為整棵樹的根使max(son[u], n - son[u])
最小的結點u
。
實際上這兩步操作可以在一次遍歷中解決。對結點u
的每乙個兒子v
,遞迴的處理v
,求出son[v]
,然後判斷是否是結點數最多的子樹,處理完所有子結點後,判斷u
是否為重心。
void dfs(int u, int fa)
res = max(res, n - son[u]);
if(res < size)
}
我們可以發現:
對於乙個點,顯然只有經過它的路徑和不經過它的路徑。
我們不考慮不經過它的路徑。
經過它的路徑的兩個端點一定它在的兩個子樹裡。
以它為根統計到它子樹節點的距離。這樣的單獨的距離或兩段距離之和一定經過這個根節點。
對於每個點我們都可以這樣計算。
可是當樹退化成一條鏈的時候。複雜度就會很高,不斷的遞迴找子樹。
所以我們需要按樹的重心(因為重心刪掉後最大子樹節點數最小)來找這些點,這樣可以把複雜度控制在 o(
nlog
n)。–
所以點分治的思想就是:
找重心把它作為根
解決根的路徑問題
遞迴子樹解決子問題(重複一二步驟)
針對每題不同的只有cal()
–
luogu p3806 【模板】點分治1
size
是以刪掉u
後,最大連通塊的大小。
size[0]
是整棵子樹大小,一開始rt = 0
,用之後更小size
值更新rt
。
sum
是當前子樹內的總點數。
temnum
是子樹編號。
cnt2
和temnum
在cal()
裡要記得每次都要清一次空。因為cnt2
每次是針對不同的重心的,而temnum
每次針對是不同重心的子樹的。
#include
using
namespace
std;
const
int n = 1e5 + 5;
struct edge e[n << 1];
struct seg seg1[n << 1];
int n, q, sum;
bool ok[10000005];
int head[n], cnt = 0;
void add(int x, int y, int z)
int cnt2 = 0;
void add2(int dis, int pos)
int rt;
int son[n], size[n], vis[n];
void dfs1(int u, int fa)
size[u] = max(size[u], sum - son[u]);
if (size[u] < size[rt]) rt = u;
}int dis[n];
void dfs2(int u, int fa, int num)
}void cal(int u)
for (int i = 1; i < cnt2; i ++)
for (int j = i + 1; j <= cnt2; j ++)
if (seg1[i].pos != seg1[j].pos)
ok[seg1[i].dis + seg1[j].dis] = 1;
}void solve(int u)
}int main()
size[0] = sum = n;
dfs1(1, 0);
solve(rt);
for (int i = 1; i <= q; i ++)
return
0;}
樹的重心 樹的直徑
樹的重心 樹的重心定義為 找到乙個點,其所有的子樹中最大的子樹節點數最少,那麼這個點就是這棵樹的重心,刪去重心後,生成的多棵樹盡可能平衡.實際上樹的重心在樹的點分治中有重要的作用,可以避免n 2的極端複雜度 從退化鏈的一端出發 保證nlogn的複雜度,利用樹型dp可以很好地求樹的重心.求樹的重心 模...
樹的直徑 樹的重心
樹的直徑 定義 那麼樹上最遠的兩個點,他們之間的距離,就被稱之為樹的直徑。樹的直徑的性質 1.直徑兩端點一定是兩個葉子節點。2.距離任意點最遠的點一定是直徑的乙個端點,這個基於貪心求直徑方法的正確性 可以得出。3.對於兩棵樹,如果第一棵樹直徑兩端點為 u,v 第二棵樹直徑兩端點為 x,y 用條邊將兩...
樹的直徑與重心
樹的直徑,指樹上最長的不重複經過同乙個點的路徑。方法 先從任意一點p pp出發,找離它最遠的點q qq,再從點q qq出發,找離它最遠的點w ww,w ww到q qq的距離就是的直徑 具體實現可以使用兩次dfs dfsdf s。演算法證明 反證法 include using namespace st...