BZOJ 4326 運輸計劃(LCA +樹上差分)
任重而道遠
Description
公元 2044 年,人類進入了宇宙紀元。L 國有 n 個星球,還有 n?1 條雙向航道,每條航道建立在兩個星球之間,
這 n-1 條航道連通了 L 國的所有星球。小 P 掌管一家物流公司, 該公司有很多個運輸計劃,每個運輸計劃形如
:有一艘物流飛船需要從 ui 號星球沿最快的宇航路徑飛行到 vi 號星球去。顯然,飛船駛過一條航道是需要時間
的,對於航道 j,任意飛船駛過它所花費的時間為 tj,並且任意兩艘飛船之間不會產生任何干擾。為了鼓勵科技
創新, L 國國王同意小 P 的物流公司參與 L 國的航道建設,即允許小P 把某一條航道改造成蟲洞,飛船駛過蟲
洞不消耗時間。在蟲洞的建設完成前小 P 的物流公司就預接了 m 個運輸計劃。在蟲洞建設完成後,這 m 個運輸
計劃會同時開始,所有飛船一起出發。當這 m 個運輸計劃都完成時,小 P 的物流公司的階段性工作就完成了。如
果小 P 可以自由選擇將哪一條航道改造成蟲洞, 試求出小 P 的物流公司完成階段性工作所需要的最短時間是多
少?
Input
第一行包括兩個正整數 n,m,表示 L 國中星球的數量及小 P 公司預接的運輸計劃的數量,星球從 1 到 n 編號。
接下來 n-1 行描述航道的建設情況,其中第 i 行包含三個整數 ai,bi 和 ti,
表示第 i 條雙向航道修建在 ai 與 bi 兩個星球之間,任意飛船駛過它所花費的時間為 ti。
接下來 m 行描述運輸計劃的情況,其中第 j 行包含兩個正整數 uj 和 vj,表示第 j 個運輸計劃是從 uj 號星球飛往 vj號星球。
資料保證 1≤ui,vi≤n ,n,m<=300000
資料保證 1≤ai,bi≤n 且 0≤ti≤1000。
Output
輸出檔案只包含一個整數,表示小 P 的物流公司完成階段性工作所需要的最短時間。
Sample Input
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5
Sample Output
11
將第 1 條航道改造成蟲洞: 則三個計劃耗時分別為:11,12,11,故需要花費的時間為 12。
將第 2 條航道改造成蟲洞: 則三個計劃耗時分別為:7,15,11,故需要花費的時間為 15。
將第 3 條航道改造成蟲洞: 則三個計劃耗時分別為:4,8,11,故需要花費的時間為 11。
將第 4 條航道改造成蟲洞: 則三個計劃耗時分別為:11,15,5,故需要花費的時間為 15。
將第 5 條航道改造成蟲洞: 則三個計劃耗時分別為:11,10,6,故需要花費的時間為 11。
故將第 3 條或第 5 條航道改造成蟲洞均可使得完成階段性工作的耗時最短,需要花費的時間為 11。
AC程式碼:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 3e5 + 5, P = 22;
struct Edge {int tov, nxt, val;} e[N << 1];
struct Node {
int u, v, lca, val;
bool operator < (const Node &a) const {
return val > a.val;
}
}q[N];
int head[N], anc[N][P + 2], w[N][P + 2], s[N], dep[N], dis[N];
int n, m, num;
int read () {
int x = 0, f = 0; char c = getchar ();
while (!isdigit (c)) f |= (c == '-'), c = getchar ();
while (isdigit (c)) x = x * 10 + c - '0', c = getchar ();
return f ? -x : x;
}
void add_edge (int u, int v, int val) {
e[++num] = (Edge) {v, head[u], val}, head[u] = num;
e[++num] = (Edge) {u, head[v], val}, head[v] = num;
}
void dfs (int u) {
dep[u] = dep[anc[u][0]] + 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].tov;
if (v == anc[u][0]) continue;
anc[v][0] = u, dis[v] = dis[u] + e[i].val;
dfs (v);
}
}
void init () {
dfs (1);
for (int p = 1; p <= P; p++)
for (int u = 1; u <= n; u++)
anc[u][p] = anc[anc[u][p - 1]][p - 1];
}
int Lca (int u, int v) {
if (dep[u] < dep[v]) swap (u, v);
for (int p = P; p >= 0; p--)
if (dep[anc[u][p]] >= dep[v]) u = anc[u][p];
if (u == v) return u;
for (int p = P; p >= 0; p--)
if (anc[u][p] != anc[v][p]) {
u = anc[u][p];
v = anc[v][p];
}
return anc[u][0];
}
void get (int u) {
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].tov;
if (v == anc[u][0]) continue;
get (v);
s[u] += s[v];
}
}
bool check (int x) {
memset (s, 0, sizeof (s));
int tot = 0, sww = 0, f = 1, jud = 0;
for (int i = 1; q[i].val > x; i++) {
s[q[i].u]++, s[q[i].v]++;
s[q[i].lca] -= 2;
tot++;
}
get (1);
for (int i = 1; i <= n; i++)
if (s[i] == tot) {
sww = max (sww, dis[i] - dis[anc[i][0]]);
jud = 1;
}
if (!jud) return false;
for (int i = 1; i <= tot; i++)
if (q[i].val - sww > x) {
f = 0; break;
}
return f;
}
int main () {
n = read (), m = read ();
for (int i = 1; i < n; i++) {
int u = read (), v = read (), val = read ();
add_edge (u, v, val);
}
init ();
for (int i = 1; i <= m; i++) {
int u = read (), v = read ();
int ca = Lca (u, v);
q[i] = (Node) {u, v, ca, dis[u] + dis[v] - 2 * dis[ca]};
}
sort (q + 1, q + m + 1);
int lf = 0, rg = q[1].val;
while (lf <= rg) {
int mid = lf + rg >> 1;
if (check (mid)) rg = mid - 1;
else lf = mid + 1;
}
printf ("%d\n", lf);
return 0;
}