树网的核 加强版,好题~
可以证明,如果存在多条直径,选取任意一条直径都是等价的。
证明:
如图,考虑两条直径 AB, CD,其中它们的重合部分为 EF
A B
\ /
\ /
\_____________________/
/E F\
/ \
/ \
C D
先证 |AE| = |CE|, |BF| = |DF|
|AE| +|EF| + |BF| = |CE| + |EF| + |DF| ⟹ |AE| + |BF| = |CE| + |DF|
若|AE| > |CE|, 则 |BF| < |DF|,那么 |AD| >|AB|
而AB为树的直径,推出矛盾,所以 |AE| <= |CE|
同理可证 |AE| >= |CE|,|BF| <= |DF|,|BF| >= |DF|
∴ |AE| = |CE|, |BF| = |DF|
现在证明,在任意一条直径上选取路径 p 计算偏心距是等价的。
如果
如果 p 覆盖 EF。
- 当
p 在AB上时,CD上的点对偏心距的贡献为 |CE| 和 |DF|- 当 p 在CD上时,AB上的点对偏心距的贡献为 |AE| 和 |BF|
∵ |AE| = |CE|, |BF| = |DF|
∴ 在任意一条直径上选取路径 p 计算偏心距是等价的综上所述,结论成立。
根据贪心的原则,在直径上选取尽量长的路径
p 可以使得偏心距最小。对于这条路径,如何计算偏心距?
记直径左端点为 L, 右端点为 R,这条路径左端点为 l ,右端点为
r 。ECC(p) = max { max { g(i) | i
∈p }, dist(L , l) , dist(r , R) }其中 g(i) 表示 i 点不经过直径能到达的最远距离。
对于
max { g(i) | i∈p },用单调队列维护 g(i) | i∈p 下降即可。时间复杂度: O(n)
/************************************************************** Problem: 1999 User: cyxhahaha Language: C++ Result: Accepted Time:1496 ms Memory:58324 kb ****************************************************************/ #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <string> #include <map> #include <vector> #include <stack> #include <queue> #include <utility> #include <iostream> #include <algorithm> template<class Num>void read(Num &x) { char c; int flag = 1; while((c = getchar()) < '0' || c > '9') if(c == '-') flag *= -1; x = c - '0'; while((c = getchar()) >= '0' && c <= '9') x = (x<<3) + (x<<1) + (c-'0'); x *= flag; return; } template<class Num>void write(Num x) { if(x < 0) putchar('-'), x = -x; static char s[20];int sl = 0; while(x) s[sl++] = x%10 + '0',x /= 10; if(!sl) {putchar('0');return;} while(sl) putchar(s[--sl]); } #define REP(__x,__st,__ed) for(int __x = (__st); __x <= (__ed); __x++) const int maxn = 5e5 + 5, Nya = -1; int n, S; struct Edge { int v, w, next; Edge(int v=0,int w=0,int next=0):v(v),w(w),next(next){} }edge[maxn<<1]; int el, head[maxn]; int dist[maxn], path[maxn], len; int Lmax[maxn], Rmax[maxn], Mmax[maxn]; bool hash[maxn]; void NewEdge(int u,int v,int w) { edge[++el] = Edge(v, w, head[u]), head[u] = el; } void dfs(int a,int fa,int arr[]) { for(int i = head[a]; i; i = edge[i].next) { int p = edge[i].v; if(p == fa) continue; arr[p] = arr[a] + edge[i].w; dfs(p, a, arr); } } int gainmax(int a,int fa) { int ret = 0; for(int i = head[a]; i ; i = edge[i].next) { int p = edge[i].v; if(p == fa || hash[p]) continue; ret = std::max(gainmax(p, a) + edge[i].w, ret); } return ret; } bool find(int a,int aim,int fa) { if(a == aim) { path[++len] = a; return true; } for(int i = head[a]; i; i = edge[i].next) { if(edge[i].v == fa) continue; if(find(edge[i].v, aim, a)) { path[++len] = a; return true; } } return false; } void init() { int u, v, w; read(n), read(S); for(int i = 1; i < n; i++) { read(u), read(v), read(w); NewEdge(u, v, w); NewEdge(v, u, w); } } void diameter() { int s = 1, t = 1; dist[1] = 0, dfs(1, 0, dist); REP(i, 1, n) if(dist[i] > dist[s]) s = i; dist[s] = 0, dfs(s, 0, dist); REP(i, 1, n) if(dist[i] > dist[t]) t = i; find(t, s, 0); REP(i, 1, len) hash[path[i]] = true; REP(i, 1, len) Mmax[i] = gainmax(path[i], 0); REP(i, 1, len) { Lmax[i] = dist[path[i]] - dist[path[1]]; Rmax[i] = dist[path[len]] - dist[path[i]]; } } void solve() { static int line[maxn]; int f = 0, r = 0, ans = Nya, cal; for(int i = 1, j = 1; i <= len; i++) { if(f != r && line[f] < i) line[f++] = 0; while(j <= n && dist[path[j]] - dist[path[i]] <= S) { while(f != r && Mmax[line[r-1]] <= Mmax[j]) line[--r] = 0; line[r++] = j++; } cal = std::max(Mmax[line[f]], std::max(Lmax[i], Rmax[j-1])); if(ans == Nya || cal < ans) ans = cal; } write(ans); } int main() { #ifndef ONLINE_JUDGE freopen("1999.in","r",stdin); freopen("1999.out","w",stdout); #endif init(), diameter(), solve(); #ifndef ONLINE_JUDGE fclose(stdin); fclose(stdout); #endif return 0; }