[Luogu P2680] [BZOJ 4326] 運輸計劃
洛谷傳送門
題目背景
公元 年,人類進入了宇宙紀元。
題目描述
L 國有 個星球,還有 條雙向航道,每條航道建立在兩個星球之間,這 條航道連通了 L 國的所有星球。
小 P 掌管一家物流公司, 該公司有很多個運輸計劃,每個運輸計劃形如:有一艘物流飛船需要從 號星球沿最快的宇航路徑飛行到 號星球去。顯然,飛船駛過一條航道是需要時間的,對於航道 ,任意飛船駛過它所花費的時間為 ,並且任意兩艘飛船之間不會產生任何干擾。
為了鼓勵科技創新, L 國國王同意小 P 的物流公司參與 L 國的航道建設,即允許小 P 把某一條航道改造成蟲洞,飛船駛過蟲洞不消耗時間。
在蟲洞的建設完成前小 的物流公司就預接了 個運輸計劃。在蟲洞建設完成後,這 個運輸計劃會同時開始,所有飛船一起出發。當這 個運輸計劃都完成時,小 P 的物流公司的階段性工作就完成了。
如果小 P 可以自由選擇將哪一條航道改造成蟲洞, 試求出小 P 的物流公司完成階段性工作所需要的最短時間是多少?
輸入輸出格式
輸入格式:
第一行包括兩個正整數 ,表示 L 國中星球的數量及小 P 公司預接的運輸計劃的數量,星球從 到 編號。
接下來 行描述航道的建設情況,其中第 行包含三個整數 和 ,表示第 條雙向航道修建在 與 兩個星球之間,任意飛船駛過它所花費的時間為 。資料保證 且 。
接下來 行描述運輸計劃的情況,其中第 行包含兩個正整數 和 ,表示第 個運輸計劃是從 號星球飛往 號星球。資料保證
輸出格式:
一個整數,表示小 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
說明
所有測試資料的範圍和特點如下表所示
請注意常數因子帶來的程式效率上的影響。
解題分析
看到這是求一個最大值的最小值, 顯然就可以二分, 將這種最值問題轉化為判定性的問題。
我們二分出答案, 就可以一遍求出哪些路徑的中包含的邊應該被刪掉。我們如何快速找到這些路徑中公共的邊? 顯然可以在樹上打標記, 一條的邊可以將和的權值++, 在的權值, 這樣我們從下往上遞推, 如果某個點的權值正好等於包含的邊的數量, 而且其上方的一條邊的權值所有路徑長度的最大值二分得到的答案, 那麼這種方案就是合法的。
程式碼如下:
#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);
}