這個題是很經典的生成樹問題。第一次接觸時對倍增演算法的理解還不夠透徹,沒能打出來正解。
首先,原題中給出的是一幅圖,詢問從某點出發到另一點「需要經過的最短邊的最大值」。用floyd來解決是可以的,但是資料範圍不能承受o(n^3)的複雜度。於是我們考慮:假設原圖是連通的,那麼我們從某點到另一點,一定至少存在一條路徑;而明顯有一條路徑是最優的,滿足它所經過的最小邊最大。既然這樣,我們能不能把這條路徑找出來呢?於是我們想對原圖做一些處理。新的圖應該滿足:
1. 圖仍是連通的。
2. 任意兩點間的一條路徑滿足上述最優條件。
於是我們想到了生成樹。從貪心的角度考慮,兩點之間一定有一條這樣的最優路徑是最大生成樹上的唯一路徑。說明:因為最大生成樹外的一條邊一定小於等於樹上的邊權,那麼它不可能比這條樹上路徑更優。
求出mst後,我們要維護的是樹上路徑的最小值資訊。路徑本身可以用lca來搞,可是路徑上的資訊怎麼預處理呢?聯想st演算法,我們雖然不能維護任意兩點間的資訊,但是可以用倍增的思想,維護w(i, k)表示節點i到它的2^k代祖先所需經過的最短路徑。w(i, 0)就是生成樹上該點入邊的權,然後按k從小到大轉移,轉移方程w(i, k) = min(w(i, k - 1), w(f[i][k-1], k-1))。其中f陣列是按倍增法求lca記錄的祖先資訊。最後查詢時,所求的ans隨lca倍增演算法更新最小值即可。
注意原圖是不連通的,我們生成的實際上是乙個森林,kruscal演算法裡維護的並查集可以幫助判斷兩個點是否在乙個聯通塊內。
**:#include
#include
#include
#include
#include
#define maxn 10010
#define maxm 50010
#define inf (int)2e9
using namespace std;
template
void read(t &x)
return;
} int n, m, q;
int head[maxn], top = 1;
struct e edge[maxn << 1];
struct pe pedge[maxm];
bool cmp(pe a, pe b)
inline void insert(int u, int v, int w) ;
head[u] = top;
} namespace ufs
int find(int x)
bool union(int u, int v)
} using namespace ufs;
void kruscal()
} }
namespace lca
} void init2()
for (int k = 1; k <= lg; ++k)
for (int i = 1; i <= n; ++i)
} int query(int u, int v)
return min(ans, min(w[0][u], w[0][v]));
} } using namespace lca;
int main() ;
} kruscal();
init2();
read(q);
while (q--)
return 0;
} ps:看了這個題以後覺得樹剖貌似是個好東西,有空學習乙個。
(7.16)樹上路徑最小邊權用樹剖維護的確是很顯然的,之後可以打一下。
(8.2)今日乙份樹剖奉上,開啟我的暑假中二學習之旅。
luogu P1967 貨車運輸
題面傳送門 顯然不可以最長路。司機肯定喜歡走長的路徑,所以先把最大生成樹跑出來。然後再最大生成樹上跑倍增不就好了?實現 include include include define min a,b a b a b using namespace std int n,m,k,x,y,ans,flag ...
Luogu P1967 貨車運輸
現在開始正式填以前欠下的一些題解。就從這道經典的noip題開始講吧。我們仔細看題目,發現要求的是圖上兩點 u,v 之間的路徑上最小值的最大值。跑dp?圖上狀態太多了,單次要 o n 的複雜度,直接t飛。我們考慮一種經典方法 將圖轉化為一顆樹來做 由於樹保證聯通,而這裡要求最大化最小值,因此我們很容易...
luogu P1967 貨車運輸
a 國有 n 座城市,編號從 1 到 n,城市之間有 m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 q 輛貨車在運輸貨物,司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重的貨物。輸入格式 輸入檔名為 truck.in。輸入檔案第一行有兩個用乙個空格隔開的整數 n,m,表示 ...