[Luogu P2680] [BZOJ 4326] [NOIP 2015 tg]運輸計劃

洛谷傳送門

BZOJ傳送門

題目背景

公元 2044 2044 年,人類進入了宇宙紀元。

題目描述

L 國有 n n 個星球,還有 n 1 n-1 條雙向航道,每條航道建立在兩個星球之間,這 n 1 n-1 條航道連通了 L 國的所有星球。

小 P 掌管一家物流公司, 該公司有很多個運輸計劃,每個運輸計劃形如:有一艘物流飛船需要從 u i u_i 號星球沿最快的宇航路徑飛行到 v i v_i 號星球去。顯然,飛船駛過一條航道是需要時間的,對於航道 j j ,任意飛船駛過它所花費的時間爲 t j t_j ,並且任意兩艘飛船之間不會產生任何干擾。

爲了鼓勵科技創新, L 國國王同意小 P 的物流公司參與 L 國的航道建設,即允許小 P 把某一條航道改造成蟲洞,飛船駛過蟲洞不消耗時間。

在蟲洞的建設完成前小 P P 的物流公司就預接了 m m 個運輸計劃。在蟲洞建設完成後,這 m m 個運輸計劃會同時開始,所有飛船一起出發。當這 m m 個運輸計劃都完成時,小 P 的物流公司的階段性工作就完成了。

如果小 P 可以自由選擇將哪一條航道改造成蟲洞, 試求出小 P 的物流公司完成階段性工作所需要的最短時間是多少?

輸入輸出格式

輸入格式:

第一行包括兩個正整數 n , m n, m ,表示 L 國中星球的數量及小 P 公司預接的運輸計劃的數量,星球從 1 1 n n 編號。

接下來 n 1 n-1 行描述航道的建設情況,其中第 i i 行包含三個整數 a i , b i a_i, b_i t i t_i ,表示第 i i 條雙向航道修建在 a i a_i b i b_i 兩個星球之間,任意飛船駛過它所花費的時間爲 t i t_i 。數據保證 1 a i , b i n 1 \leq a_i,b_i \leq n 0 t i 1000 0 \leq t_i \leq 1000

接下來 m m 行描述運輸計劃的情況,其中第 j j 行包含兩個正整數 u j u_j v j v_j ,表示第 j j 個運輸計劃是從 u j u_j 號星球飛往 v j v_j 號星球。數據保證 1 u i , v i n 1 \leq u_i,v_i \leq n

輸出格式:

一個整數,表示小 PP 的物流公司完成階段性工作所需要的最短時間。

輸入輸出樣例

輸入樣例#1:

6 3 
1 2 3 
1 6 4 
3 1 7 
4 3 6 
3 5 5 
3 6 
2 5 
4 5

輸出樣例#1:

11

說明

所有測試數據的範圍和特點如下表所示

img

請注意常數因子帶來的程序效率上的影響。

解題分析

看到這是求一個最大值的最小值, 顯然就可以二分, 將這種最值問題轉化爲判定性的問題。

我們二分出答案, 就可以一遍求出哪些路徑的中包含的邊應該被刪掉。我們如何快速找到這些路徑中公共的邊? 顯然可以在樹上打標記, 一條 u v u\to v 的邊可以將 u u v v 的權值++, 在 l c a ( u , v ) lca(u,v) 的權值 2 -2 , 這樣我們從下往上遞推, 如果某個點的權值正好等於包含的邊的數量, 而且其上方的一條邊的權值 > > 所有路徑長度的最大值 - 二分得到的答案, 那麼這種方案就是合法的。

代碼如下:

#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <cctype> #include <cmath> #define R register #define IN inline #define W while #define gc getchar() #define MX 300050 template <class T> IN void in(T &x) { x = 0; R char c = gc; for (; !isdigit(c); c = gc); for (; isdigit(c); c = gc) x = (x << 1) + (x << 3) + c - 48; } int head[MX], dep[MX], son[MX], topf[MX], fat[MX], siz[MX], d[MX]; int from[MX], to[MX], dis[MX], sum[MX], lca[MX], eval[MX], ord[MX]; int dot, line, tot, cnt, mpat, msin; struct Edge {int to, val, nex;} edge[MX << 1]; IN void add(R int fr, R int to, R int val) {edge[++cnt] = {to, val, head[fr]}, head[fr] = cnt;} void DFS(R int now) { siz[now] = 1; for (R int i = head[now]; i; i = edge[i].nex) { if(edge[i].to == fat[now]) continue; dep[edge[i].to] = dep[now] + 1; d[edge[i].to] = d[now] + edge[i].val; fat[edge[i].to] = now; eval[edge[i].to] = edge[i].val; DFS(edge[i].to); siz[now] += siz[edge[i].to]; if(siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to; } ord[++tot] = now; } void DFS(R int now, R int grand) { topf[now] = grand; if(!son[now]) return; DFS(son[now], grand); for (R int i = head[now]; i; i = edge[i].nex) { if(edge[i].to == fat[now] || edge[i].to == son[now]) continue; DFS(edge[i].to, edge[i].to); } } IN int get(R int x, R int y) { W (topf[x] ^ topf[y]) { if(dep[topf[x]] < dep[topf[y]]) std::swap(x, y); x = fat[topf[x]]; } return dep[x] < dep[y] ? x : y; } IN bool check(R int ans) { std::memset(sum, tot = 0, sizeof(sum)); for (R int i = 1; i <= line; ++i) if(dis[i] > ans) sum[from[i]]++, sum[to[i]]++, sum[lca[i]] -= 2, ++tot; for (R int i = 1; i <= dot; ++i) { sum[fat[ord[i]]] += sum[ord[i]]; if(eval[ord[i]] >= mpat - ans && sum[ord[i]] == tot) return true; } return false; } IN void solve(R int lef, R int rig) { R int mid, ans; W (lef <= rig) { mid = lef + rig >> 1; if(check(mid)) ans = mid, rig = mid - 1; else lef = mid + 1; } printf("%d", ans); } int main(void) { int a, b, c; in(dot), in(line); for (R int i = 1; i < dot; ++i) in(a), in(b), in(c), add(a, b, c), add(b, a, c), msin = std::max(msin, c); DFS(1); DFS(1, 1); for (R int i = 1; i <= line; ++i) { in(from[i]), in(to[i]); lca[i] = get(from[i], to[i]); dis[i] = d[from[i]] + d[to[i]] - (d[lca[i]] << 1); mpat = std::max(mpat, dis[i]); } solve(0, mpat); } #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <cctype> #include <cmath> #define R register #define IN inline #define W while #define gc getchar() #define MX 300050 template <class T> IN void in(T &x) { x = 0; R char c = gc; for (; !isdigit(c); c = gc); for (; isdigit(c); c = gc) x = (x << 1) + (x << 3) + c - 48; } int head[MX], dep[MX], son[MX], topf[MX], fat[MX], siz[MX], d[MX]; int from[MX], to[MX], dis[MX], sum[MX], lca[MX], eval[MX], ord[MX]; int dot, line, tot, cnt, mpat, msin; struct Edge {int to, val, nex;} edge[MX << 1]; IN void add(R int fr, R int to, R int val) {edge[++cnt] = {to, val, head[fr]}, head[fr] = cnt;} void DFS(R int now) { siz[now] = 1; for (R int i = head[now]; i; i = edge[i].nex) { if(edge[i].to == fat[now]) continue; dep[edge[i].to] = dep[now] + 1; d[edge[i].to] = d[now] + edge[i].val; fat[edge[i].to] = now; eval[edge[i].to] = edge[i].val; DFS(edge[i].to); siz[now] += siz[edge[i].to]; if(siz[edge[i].to] > siz[son[now]]) son[now] = edge[i].to; } ord[++tot] = now; } void DFS(R int now, R int grand) { topf[now] = grand; if(!son[now]) return; DFS(son[now], grand); for (R int i = head[now]; i; i = edge[i].nex) { if(edge[i].to == fat[now] || edge[i].to == son[now]) continue; DFS(edge[i].to, edge[i].to); } } IN int get(R int x, R int y) { W (topf[x] ^ topf[y]) { if(dep[topf[x]] < dep[topf[y]]) std::swap(x, y); x = fat[topf[x]]; } return dep[x] < dep[y] ? x : y; } IN bool check(R int ans) { std::memset(sum, tot = 0, sizeof(sum)); for (R int i = 1; i <= line; ++i) if(dis[i] > ans) sum[from[i]]++, sum[to[i]]++, sum[lca[i]] -= 2, ++tot; for (R int i = 1; i <= dot; ++i) { sum[fat[ord[i]]] += sum[ord[i]]; if(eval[ord[i]] >= mpat - ans && sum[ord[i]] == tot) return true; } return false; } IN void solve(R int lef, R int rig) { R int mid, ans; W (lef <= rig) { mid = lef + rig >> 1; if(check(mid)) ans = mid, rig = mid - 1; else lef = mid + 1; } printf("%d", ans); } int main(void) { int a, b, c; in(dot), in(line); for (R int i = 1; i < dot; ++i) in(a), in(b), in(c), add(a, b, c), add(b, a, c), msin = std::max(msin, c); DFS(1); DFS(1, 1); for (R int i = 1; i <= line; ++i) { in(from[i]), in(to[i]); lca[i] = get(from[i], to[i]); dis[i] = d[from[i]] + d[to[i]] - (d[lca[i]] << 1); mpat = std::max(mpat, dis[i]); } solve(0, mpat); }