题目连接:https://www.luogu.com.cn/problem/P2680
题目大意:
如果X时间可以完成,那么Y>X时间一定可以完成。满足单调性,二分时间。
对于一个时间mid。比mid大的k条路径都必须经过一次拆边变得<=mid。那么这条边一个经过这k条路径。这个用树上边差分判断。在所有经过这k条路径的边中找到最大的一条ans。如果这前k条路径-ans<=mid。L=mid+1。
否则: R=mid-1。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
struct node{
int to, c;
};
vector<node> v[300005];
//深度 //倍增 //s: 和根节点的距离 c:差分数组 fs:第i个节点代表的那条边
int d[300005], f[300005][25], lg[300005], s[300005], c[300005], fs[300005];
int tot;
struct eg{
int u, to, s;
};
vector<eg> e;
void dfs(int u, int fa){
d[u]=d[fa]+1;
f[u][0]=fa;
for(int i=1; (1<<i)<=d[u]; i++){
f[u][i]=f[f[u][i-1]][i-1];
}
for(int i=0; i<v[u].size(); i++){
if(v[u][i].to!=fa){
s[v[u][i].to]=s[u]+v[u][i].c;
dfs(v[u][i].to, u);
fs[v[u][i].to]=v[u][i].c;
}
}
}
int LCA(int x ,int y){
if(d[x]<d[y]){
swap(x, y);
}
while(d[x]!=d[y]){
x=f[x][lg[d[x]-d[y]]-1];
}
if(x==y){
return x;
}
for(int k=lg[d[x]]; k>=0; k--){
if(f[x][k]!=f[y][k]){
x=f[x][k], y=f[y][k];
}
}
return f[x][0];
}
int cmp(eg a, eg b){
return a.s>b.s;
}
int ans=0;
void get_max(int u, int fa, int k){//得到是前k条路径都经过的边的最大值
for(int i=0; i<v[u].size(); i++){
int to=v[u][i].to;
if(to!=fa){
get_max(to, u, k);
c[u]+=c[to];
c[to]=0;//初始化回去
}
}
if(c[u]==k){
ans=max(fs[u], ans);
}
}
int slove(int mid, int n){
int k=e.size();
for(int i=0; i<e.size(); i++){//找到比mid大的k条边
if(e[i].s<=mid){
k=i;
break;
}
}
for(int i=0; i<k; i++){//树上边差分
int a=e[i].u, b=e[i].to;
c[a]++, c[b]++, c[LCA(a, b)]-=2;
}
ans=0;
get_max(1, 0, k);//找到经过这k条路径边的最大值
for(int i=0; i<k; i++){
if(e[i].s-ans>mid){
return 0;
}
}
return 1;
}
int main(){
int n, m, a, b, c;
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) {//预处理
lg[i]=lg[i-1];
if(i==1<<lg[i-1])lg[i]++;
}
for(int i=1; i<n; i++){
scanf("%d%d%d", &a, &b, &c);
v[a].push_back(node{b, c});
v[b].push_back(node{a, c});
}
s[1]=0;
dfs(1,0);//从树根开始dfs
for(int i=1; i<=m; i++){
scanf("%d%d", &a, &b);
e.push_back(eg{a, b, s[a]+s[b]-2*s[LCA(a, b)]});
}
sort(e.begin(), e.end(), cmp);
//L:边的最大值为1000, 所以路径最多减小1000
//还有,不加这个优化会T掉一个点(各种 卡常+优化 都过不去
int R=e[0].s, L=max(0, R-1000), k;
while(L<=R){
int mid=(L+R)/2;
if(slove(mid, n)){
R=mid-1; k=mid;
}
else{
L=mid+1;
}
}
printf("%d\n", k);
return 0;
}