题目描述
公元 2044 2044 2044 年,人类进入了宇宙纪元。
L L L 国有 n n n 个星球,还有 n − 1 n−1 n−1 条双向航道,每条航道建立在两个星球之间,这 n − 1 n - 1 n−1 条航道连通了 L L L 国的所有星球。
小 P P P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形>如:有一艘物流飞船需要从 u i u_i ui 号星球沿最快的宇航路径飞行到 v i v_i vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j j j,任意飞船驶过它所花费的时间为 t j t_j tj ,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新, L L L 国国王同意小 P P P 的物流公司参与 L L L 国的航道建设,即允许小 P P P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 P P P 的物流公司就预接了 m m m 个运输计划。在虫洞建设完成后,这 m m m 个运输计划会同时开始,所有飞船一起出发。当这 m m m 个运输计划都完成时,小 P P P 的物流公司的阶段性工作就完成了。
如果小 P P P 可以自由选择将哪一条航道改造成虫洞,试求出小 P P P 的物流公司完成阶段性工作所需要的最短时间是多少?
输入格式
第一行包括两个正整数 n n n、 m m m,表示 L L L 国中星球的数量及小 P P P 公司预接的运输计划的数量,星球从 1 1 1 到 n n n 编号。
接下来 n − 1 n - 1 n−1 行描述航道的建设情况,其中第 i i i 行包含三个整数 a i a_i ai 、 b i b_i bi 和 t i t_i ti ,表示第 i i i 条双向航道修建在 a i a_i ai 与 b i b_i bi 两个星球之间,任意飞船驶过它所花费的时间为 t i t_i ti 。 接下来 m m m 行描述运输计划的情况,其中第 j j j 行包含两个正整数 u j u_j uj 和 v j v_j vj,表示第 j j j 个运输计划是从 u j u_j uj 号星球飞往 v j v_j vj 号星球。
输出格式
共 1 1 1 行,包含 1 1 1 个整数,表示小 P P P 的物流公司完成阶段性工作所需要的最短时间。
样例输入
6 6 6 3 3 3
1 1 1 2 2 2 3 3 3
1 1 1 6 6 6 4 4 4
3 3 3 1 1 1 7 7 7
4 4 4 3 3 3 6 6 6
3 3 3 5 5 5 5 5 5
3 3 3 6 6 6
2 2 2 5 5 5
4 4 4 5 5 5
样例输出
11 11 11
数据范围与提示
对于 100 100 100% 的数据, 100 ≤ n ≤ 300000 , 1 ≤ m ≤ 300000 100≤n≤300000,1≤m≤300000 100≤n≤300000,1≤m≤300000。
解析
我们会发现题目让我们求的是清空一条边的边权后
m
m
m条路径中最长路径的最小值。
引用某老师的说法, 这种题目99%都是二分答案嘛。
证明答案的二分性:
如果答案为
x
x
x可以,那么比
x
x
x大的答案都是合法的。
如果答案为
x
x
x不行,那么比
x
x
x小的答案都是非法的。
那么只要二分这个答案并且在
O
(
n
)
O(n)
O(n)的时间内检验即可。
如果有
c
n
t
cnt
cnt条路径的长度
>
m
i
d
>mid
>mid,那么去掉的边必须是这
c
n
t
cnt
cnt条边的并集。
证明:
如果去掉的边不是这 c n t cnt cnt条边的并集。那么一定存在一条路径 s s s的路径长度任然 > m i d >mid >mid,使得答案不成立。
那么我们可以得出答案
m
i
d
mid
mid成立条件的结论:
存在一条边
e
e
e使得所有的长度超过
m
i
d
mid
mid的路径都经过
e
e
e,且这些路径中的最大长度
m
a
x
l
e
n
maxlen
maxlen满足
m
a
x
l
e
n
−
v
a
l
e
≤
m
i
d
maxlen-val_e≤mid
maxlen−vale≤mid
具体操作:
- 将边权转换成点权,已 1 1 1为根进行 d f s dfs dfs后将边权置于深度较深的点。
- 求出从 1 1 1到每个点 u u u的距离 d u d_u du,这样树上任意两点 u , v u,v u,v间的距离 = d u + d v − 2 ∗ d l c a ( u , v ) =d_u+d_v-2*d_{lca(u,v)} =du+dv−2∗dlca(u,v)
- 树上差分统计经过边 e e e的路径个数时,只需要在 u u u, v v v处各 + 1 +1 +1,在 L C A ( u , v ) LCA(u,v) LCA(u,v)处 − 2 -2 −2
代码
#include<cstdio>
using namespace std;
const int maxn = 300005;
const int maxe = 600010;
const int maxm = 300005;
const int oo = 1000000000;
int n , m;
int edgenum;
int Next[maxe] , vet[maxe] , val[maxe] , head[maxn];
int w[maxn] , d[maxn];
int dep[maxn] , f[maxn][20];
int cnt[maxn] , sum[maxn];
struct path{int u , v , lca , dis;}rd[maxm];
void swap(int &x , int &y){x ^= y , y ^= x , x ^= y;}
int max(int x , int y){return x > y ? x : y;}
int min(int x , int y){return x < y ? x : y;}
int read()
{
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
int res = 0;
while(ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48) , ch = getchar();
return res;
}
void add_edge(int u , int v , int cost)
{
Next[++edgenum] = head[u];
vet[edgenum] = v;
val[edgenum] = cost;
head[u] = edgenum;
}
void dfs(int u , int fa)
{
dep[u] = dep[fa] + 1;
for(int i = 1;i <= 18;++i) f[u][i] = f[f[u][i - 1]][i - 1];
for(int e = head[u];e;e = Next[e])
{
int v = vet[e];
if(v == fa) continue;
d[v] = d[u] + val[e];
w[v] = val[e];
f[v][0] = u;
dfs(v , u);
}
}
int LCA(int u , int v)
{
if(dep[u] < dep[v]) swap(u , v);
for(int i = 18;i >= 0;--i)
{
if(dep[f[u][i]] >= dep[v]) u = f[u][i];
if(u == v) return u;
}
for(int i = 18;i >= 0;--i)
if(f[u][i] != f[v][i]) u = f[u][i] , v = f[v][i];
return f[u][0];
}
int dis(int u , int v){return d[u] + d[v] - (d[LCA(u , v)] << 1);}
void add(int u , int v , int lca){cnt[u]++ , cnt[v]++ , cnt[lca] -= 2;}
void calc(int u , int fa)
{
sum[u] = cnt[u];
for(int e = head[u];e;e = Next[e])
{
int v = vet[e];
if(v == fa) continue;
calc(v , u);
sum[u] += sum[v];
}
}
bool check(int dist)
{
for(int i = 1;i <= n;++i) cnt[i] = 0;
int num = 0 , max_dis = 0;
for(int i = 1;i <= m;++i)
if(rd[i].dis > dist)
{
add(rd[i].u , rd[i].v , rd[i].lca);
num++;
max_dis = max(max_dis , rd[i].dis);
}
calc(1 , 0);
for(int i = 1;i <= n;++i)
if(sum[i] == num && max_dis - w[i] <= dist) return 1;
return 0;
}
int main()
{
n = read() , m = read();
for(int i = 1;i < n;++i)
{
int u = read() , v = read() , cost = read();
add_edge(u , v , cost);
add_edge(v , u , cost);
}
dfs(1 , 0);
int r = 0;
for(int i = 1;i <= m;++i)
{
int u = read() , v = read() , dist = dis(u , v);
rd[i] = (path){u , v , LCA(u , v) , dist};
r = max(r , dist);
}
int l = 0;
while(l < r)
{
int mid = (l + r - 1) >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n",r);
return 0;
}