爲作這道題,特地去學了一波Kruskal重構樹。
如下寫寫學習心得:
首先像Kruskal同樣按權值排序,
不過將Kruskal生成樹的並查集合並操做改成了新建點,爲合併的兩點的父親,點權爲加入的邊的權值的操做
做者不要臉地剽圖了
變爲:
有一些性質:
1.二叉樹。
2.點權大根堆(Kruskal從小到大的排序,從小到大則反之)
3.兩點間的最大距離爲LCA的權值--->兩點可否經過邊權<=k的邊互達:倍增跳到LCA是否小於等於k
例題1:html
路徑權值
Description
給定一個帶權樹,樹上任意兩點間的路徑權值d(x,y)定義爲x,y這兩個點之間路徑上的最小值,樹上任意一點x的權值定義爲這個點到樹上其餘全部點的路徑權值和,即\(\sum_{i=1}^{n} d(x,i)\),現求樹上一點,使得這個點的權值最大,輸出這個值。
Input
首先輸入一個整數Q,接着每組數據首先輸入一個整數 n(1≤n≤100000),表示該組數1據中樹的點的個數。
接下來n−1行,每行三個整數 x,y,s(1≤x,y≤n,1≤s≤1000),表示編號爲x的節點和編號爲y的節點之間存在一條權值爲s的邊,樹上每一個點的編號爲1~n
Output
對於每組數據,首先輸出數據編號,而後輸出樹上的點的最大權值,具體格式見輸出樣例。
Sample Input
2
4
1 2 2
2 4 1
2 3 1
4
1 2 1
2 4 1
2 3 1
Sample Output
Case 1: 4
Case 2: 3數組
lca的點權就是左右子樹之間的最大邊權,
因此遇到每個非葉子節點x,就對左子樹加上siz[rc]w[x],右子樹加上siz[lc]w[x]的權值,區間加法,用樹狀數組維護。
上文說的這道題就是例題2,
下面給出代碼:學習
int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);} bool merge(int u,int v,int w){ u=getf(u),v=getf(v); if(u==v) return false; ++tot,f[u]=f[v]=f[tot]=tot,add(tot,u),add(tot,v); fa[u][0]=fa[v][0]=tot,maxx[u][0]=maxx[v][0]=w; return true; }
for(int i=1;i<=20;++i) fa[x][i]=fa[fa[x][i-1]][i-1],maxx[x][i]=max(maxx[x][i-1],maxx[fa[x][i-1]][i-1]);