题目描述
公元 204420442044 年,人类进入了宇宙纪元。
LLL 国有 nnn 个星球,还有 n−1n−1n−1 条双向航道,每条航道建立在两个星球之间,这 n−1n - 1n−1 条航道连通了 LLL 国的所有星球。
小 PPP 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形>如:有一艘物流飞船需要从 uiu_iui 号星球沿最快的宇航路径飞行到 viv_ivi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 jjj,任意飞船驶过它所花费的时间为 tjt_jtj ,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新,LLL 国国王同意小 PPP 的物流公司参与 LLL 国的航道建设,即允许小 PPP 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小 PPP 的物流公司就预接了 mmm 个运输计划。在虫洞建设完成后,这 mmm 个运输计划会同时开始,所有飞船一起出发。当这 mmm 个运输计划都完成时,小 PPP 的物流公司的阶段性工作就完成了。
如果小 PPP 可以自由选择将哪一条航道改造成虫洞,试求出小 PPP 的物流公司完成阶段性工作所需要的最短时间是多少?
输入格式
第一行包括两个正整数 nnn、mmm,表示 LLL 国中星球的数量及小 PPP 公司预接的运输计划的数量,星球从 111 到 nnn 编号。
接下来 n−1n - 1n−1 行描述航道的建设情况,其中第 iii 行包含三个整数 aia_iai 、bib_ibi 和 tit_iti ,表示第 iii 条双向航道修建在 aia_iai 与 bib_ibi 两个星球之间,任意飞船驶过它所花费的时间为 tit_iti 。 接下来 mmm 行描述运输计划的情况,其中第 jjj 行包含两个正整数 uju_juj 和 vjv_jvj,表示第 jjj 个运输计划是从 uju_juj 号星球飞往 vjv_jvj 号星球。
输出格式
共 111 行,包含 111 个整数,表示小 PPP 的物流公司完成阶段性工作所需要的最短时间。
样例输入
666 333
111 222 333
111 666 444
333 111 777
444 333 666
333 555 555
333 666
222 555
444 555
样例输出
111111
数据范围与提示
对于 100100100% 的数据,100≤n≤300000,1≤m≤300000100≤n≤300000,1≤m≤300000100≤n≤300000,1≤m≤300000。
解析
我们会发现题目让我们求的是清空一条边的边权后mmm条路径中最长路径的最小值。
引用某老师的说法, 这种题目99%都是二分答案嘛。
证明答案的二分性:
如果答案为xxx可以,那么比xxx大的答案都是合法的。
如果答案为xxx不行,那么比xxx小的答案都是非法的。
那么只要二分这个答案并且在O(n)O(n)O(n)的时间内检验即可。
如果有cntcntcnt条路径的长度>mid>mid>mid,那么去掉的边必须是这cntcntcnt条边的并集。
证明:
如果去掉的边不是这cntcntcnt条边的并集。那么一定存在一条路径sss的路径长度任然>mid>mid>mid,使得答案不成立。
那么我们可以得出答案midmidmid成立条件的结论:
存在一条边eee使得所有的长度超过midmidmid的路径都经过eee,且这些路径中的最大长度maxlenmaxlenmaxlen满足maxlen−vale≤midmaxlen-val_e≤midmaxlen−vale≤mid
具体操作:
- 将边权转换成点权,已111为根进行dfsdfsdfs后将边权置于深度较深的点。
- 求出从111到每个点uuu的距离dud_udu,这样树上任意两点u,vu,vu,v间的距离=du+dv−2∗dlca(u,v)=d_u+d_v-2*d_{lca(u,v)}=du+dv−2∗dlca(u,v)
- 树上差分统计经过边eee的路径个数时,只需要在uuu,vvv处各+1+1+1,在LCA(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;
}