题意
思考
这题题意简要来讲就是:给定树上许多条链,求删掉一条边后,所有链的最大值最小是多少
首先,各个点对间的距离可以用lca求出,主要问题是怎样考虑这个删边,删除一条边后会有以下两种情况:
- 取得最大值的点对间经过该边
- 取得最大值的点对间不经过该边
这样就很难处理了,我们可以考虑二分答案,只考虑所有距离大于该值的点对,最后判断删边后所有大于该值的点对是否小于该值即可,删边当然是删掉所有大于该值的点对的公共边中的最大值啦。如何记录最大公共边?直接树上差分最后跑一边dfs就好~(不过最后吐槽一句:这题太卡常了,我现在luogu还有一个点被卡)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x * f;
}
const int M = 300030;
const int N = 300030;
struct node{
int nxt, from, to, dis, LCA;
}edge[M << 1], E[M];
int head[N], num;
void build(int from, int to, int dis){
edge[++num].nxt = head[from];
edge[num].to = to;
edge[num].dis = dis;
head[from] = num;
}
bool cmp(node a, node b){ return a.dis < b.dis; }
int d[N], f[N][20], dis[N];
void dfs(int u, int fa){
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa) continue;
d[v] = d[u] + 1; f[v][0] = u; dis[v] = dis[u] + edge[i].dis;
dfs(v, u);
}
}
int lca(int u, int v){
if(d[u] < d[v]) swap(u, v);
for(int i=18; i>=0; i--) if(d[f[u][i]] >= d[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 n, m, val[N];
void dfs2(int u, int fa){
for(int i=head[u]; i; i=edge[i].nxt){
int v = edge[i].to;
if(v == fa) continue;
dfs2(v, u);
val[u] += val[v];
}
}
bool check(int now){
memset(val, 0, sizeof(val)); int js = 0;
for(int i=1; i<=m; i++){
if(E[i].dis > now) val[E[i].from] ++, val[E[i].to] ++, val[E[i].LCA] -= 2, js ++;
}
dfs2(1, 0);
int nowdis = 0;
for(int i=1; i<=n; i++){
if(val[i] == js) nowdis = max(nowdis, dis[i] - dis[f[i][0]]);
}
for(int i=1; i<=m; i++){
if(E[i].dis - nowdis <= now) continue;
else return 0;
}
return 1;
}
int main(){
n = read(); m = read();
for(int i=1; i<=n-1; i++){
int u = read(), v = read(), d = read();
build(u, v, d); build(v, u, d);
}
d[1] = 1; dfs(1, 0);
for(int j=1; j<=18; j++)
for(int i=1; i<=n; i++) f[i][j] = f[f[i][j-1]][j-1];
int MAX = 0;
for(int i=1; i<=m; i++){
E[i].from = read(), E[i].to = read();
E[i].LCA = lca(E[i].from, E[i].to);
E[i].dis = dis[E[i].from] + dis[E[i].to] - 2 * dis[E[i].LCA];
MAX = max(MAX, E[i].dis);
}
int l = 0, r = MAX, ANS = 0;
while(l <= r){
int mid = (l + r) >> 1;
if( check(mid) ){
ANS = mid;
r = mid - 1;
}
else l = mid + 1;
}
cout << ANS;
return 0;
}
总结
对于这一题,二分答案的原因是我们并不确定也不能直接算出最终的答案(因为有不同的情况),通过二分答案我们可以确定一个界限,方便我们求解。