題解 P1099 【樹網的核】
轉載註明來源:https://www.cnblogs.com/syc233/p/13693027.html
樹的直徑+尺取法。
題意
給定一棵帶邊權無根樹,在其直徑上求出一段長度不超過 \(s\) 的路徑 \(F\) ,使得離路徑距離最遠的點到路徑的距離最短。
題解
首先,在 \(s\) 的限制下,\(F\) 應儘量長。這個畫下圖就不難明白。
先求出直徑,令直徑的兩端點分別為 \(x,y\) 。
定義一個關於直徑上點 \(u\) 的函式 \(dis(u)\) ,表示除直徑上的點到 \(u\) 的最遠距離。那麼就有一個結論:
對於任意一個直徑上的點 \(u\) ,有 \(dis(u)\leq d(u,x),dis(u)\leq d(u,y)\)
證明:
若存在一個直徑上的點滿足 \(dis(u)>d(u,x)\) ,那麼有 \(dis(u)+d(u,y)>d(u,x)+d(u,y)\) 。顯而易見, 此時 \(x,y\) 不可能是直徑的兩個端點。
同理,\(dis(u)>d(u,y)\) 同樣不合法。
證畢。
考慮一條路徑,如何計算它的偏心距。設路徑 \(F\) 的兩端點分別為 \(a,b\) ,根據偏心距的定義,可以重寫一下題目中給出的式子:
\[{\rm{ECC}}(F)=\max\{\max\{dis(u),u\in F\},d(x,a),d(b,y)\} \]
設直徑為 \(L\) 。對於點 \(v\) ,滿足 \(v\in L,v \not \in F\)
\[dis(v)\leq d(x,v)\leq d(x,a) \]
那麼可以重寫一下上面的式子:
\[{\rm{ECC}}(F)=\max\{\max\{dis(u),u\in L\},d(x,a),d(b,y)\} \]
發現 \(\max\{dis(u),u\in L\}\) 與 \(F\) 無關,那麼我們可以在直徑上尺取求出所有 \(F\) 中 \(\max\{d(x,a),d(b,y)\}\) 的最小值,再與 \(\max\{dis(u),u\in L\}\) 取max即可。
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 305
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
template <typename T>
inline void read(T &x)
{
x=0;T f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
x*=f;
}
struct edge
{
int u,v,w,next;
edge(int u,int v,int w,int next):u(u),v(v),w(w),next(next){}
edge(){}
}e[maxn<<1];
int head[maxn],k;
inline void add(int u,int v,int w)
{
e[k]=edge(u,v,w,head[u]);
head[u]=k++;
}
int n,s;
int dis[maxn],fro[maxn];
bool vis[maxn];
inline int dfs(int u,int fa)
{
int res=u;
for(int i=head[u];~i;i=e[i].next)
{
int v=e[i].v;
if(v==fa||vis[v]) continue;
fro[v]=u;
dis[v]=dis[u]+e[i].w;
int x=dfs(v,u);
if(dis[x]>dis[res]) res=x;
}
return res;
}
int main()
{
// freopen("P1099.in","r",stdin);
read(n),read(s);
memset(head,-1,sizeof(head));
for(int i=1,u,v,w;i<n;++i)
{
read(u),read(v),read(w);
add(u,v,w);
add(v,u,w);
}
int x=dfs(1,0);dis[x]=fro[x]=0;
int y=dfs(x,0),ans=INF;
for(int i=y,j=y;i;i=fro[i])
{
while(fro[j]&&dis[i]-dis[fro[j]]<=s) j=fro[j];
ans=min(ans,max(dis[j],dis[y]-dis[i]));
vis[i]=true;
}
for(int i=y;i;i=fro[i])
{
dis[i]=0;
int j=dfs(i,0);
ans=max(ans,dis[j]);
}
printf("%d\n",ans);
return 0;
}