[CTSC2017]网络 题解
Solution
可以证明的是,答案两个端点一定在直径上面。然后我们列出式子,拆开绝对值,直接二分,用单调栈维护,确定 \(a,b\) 范围,判断是否合法。
复杂度 \(\Theta(n\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 100005
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
int n,L;
#define pii pair<int,int>
#define se second
#define fi first
vector <pii> g[MAXN];
bool vis[MAXN];
int N,root,dis[MAXN],pre[MAXN],len[MAXN],dit[MAXN];
void dfs (int u,int fa){
if (dis[u] > dis[root]) root = u;
for (pii it : g[u]) if (it.fi != fa && !vis[it.fi]) dis[it.fi] = dis[u] + it.se,pre[it.fi] = u,dfs (it.fi,u);
}
/*
若答案为(a,b),那么对于任意i,j,需要满足:
min(dit[i]+dit[j]+len[j]-len[i],dit[i]+dit[j]+|len[i]-len[a]|+|len[j]-len[b]|+L)<=S
考虑对于前者条件不符合的情况,判断后者,需要满足:
dit[i]+dit[j]+len[i]-len[a]+len[j]-len[b]+L<=S =>
dit[i]+dit[j]+len[i]+len[j]+L-S<=+len[a]+len[b]
dit[i]+dit[j]-len[i]+len[a]+len[j]-len[b]+L<=S =>
dit[i]+dit[j]-len[i]+len[j]+L-S<=-len[a]+len[b]
dit[i]+dit[j]+len[i]-len[a]-len[j]+len[b]+L<=S =>
dit[i]+dit[j]+len[i]-len[j]+L-S<=+len[a]-len[b]
dit[i]+dit[j]-len[i]+len[a]-len[j]+len[b]+L<=S =>
dit[i]+dit[j]-len[i]-len[j]+L-S<=-len[a]-len[b]
*/
#define inf 4e18
int sta[MAXN];
bool check (int S){//判断答案<=S的合法性
// cout << S << endl;
int head = 1,tail = 0;
int mxv0 = -inf,mxv1 = -inf,s00 = -inf,s01 = -inf,s10 = -inf,s11 = -inf;
for (Int i = 1;i <= N;++ i){
while (head <= tail && (len[i] + dit[i] + dit[sta[head]] - len[sta[head]]) > S)
chkmax (mxv0,dit[sta[head]] + len[sta[head]]),
chkmax (mxv1,dit[sta[head]] - len[sta[head]]),head ++;
int res = dit[i] + L - S;
chkmax (s11,len[i] + res + mxv0),chkmax (s01,-len[i] + res + mxv0),
chkmax (s00,-len[i] + res + mxv1),chkmax (s10,len[i] + res + mxv1);
while (head <= tail && dit[sta[tail]] - len[sta[tail]] < dit[i] - len[i]) tail --;
sta[++ tail] = i;
}
int p01 = 1,p11 = N + 1,p10 = 0,p00 = N;
for (Int i = 1;i <= N;++ i){
while (p01 <= N && len[p01] - len[i] < s01) p01 ++;//[p01,N]合法
while (p11 >= 1 && len[p11 - 1] + len[i] >= s11) p11 --;//[p11,N]合法
while (p10 <= N && -len[p10 + 1] + len[i] >= s10) p10 ++;//[1,p10]合法
while (p00 >= 1 && -len[p00] - len[i] < s00) p00 --;//[1,p00]合法
if (max (p01,p11) <= min (p10,p00)) return 1;
}
return 0;
}
signed main(){
// freopen ("1.in","r",stdin);
while (~scanf ("%lld%lld",&n,&L) && n && L){
for (Int i = 1;i <= n;++ i) g[i].clear (),vis[i] = 0;N = 0;
for (Int i = 2,u,v,w;i <= n;++ i) read (u,v,w),g[u].push_back ({v,w}),g[v].push_back ({u,w});
dis[root] = pre[root] = 0,root = 1,dfs (1,0),dis[root] = 0,pre[root] = 0,dfs (root,0);int rt = root,mxv = dis[rt];
for (Int u = rt;u;u = pre[u]) vis[u] = 1;
for (Int u = rt;u;u = pre[u]) len[++ N] = mxv - dis[u],root = 0,dis[u] = 0,dfs (u,0),dit[N] = dis[root];
int l = 0,r = inf,ans = 0;
while (l <= r){
int mid = l + r >> 1;
if (check (mid)) ans = mid,r = mid - 1;
else l = mid + 1;
}
write (ans),putchar ('\n');
}
return 0;
}