Luogu_P1099 树网的核
### 树的直径
题目链接题面好长,都不想看
又是明明显显的树的直径的题
可以有很多种答案更新方法:
方法一
可以\(O(n^2)\)的枚举核的一端\(p\)得出\(p+s\)和离他们的最远的点
方法二
可以二分偏心距,\(O(nlogsum)\)
方法三
求出最长链,然后分别求他们的最远点的距离\(fr[i]\)
\(a[i]\)存的是链的点编号
然后偏心距就是\(\max(fr[a[k]]\max(d[a[i]],d[a[1]]-d[a[j]]))\)
再娶个\(\min\)就\(O(n)\)解决了。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
int fs,d[maxn],n,s,head[maxn],tot,fa[maxn],v[maxn],a[maxn],cnt;
int fr[maxn];
struct node{
int nxt,to,dis;
#define nxt(x) e[x].nxt
#define to(x) e[x].to
#define dis(x) e[x].dis
}e[maxn<<1];
inline void add(int from,int to,int dis){
to(++tot)=to;dis(tot)=dis;
nxt(tot)=head[from];head[from]=tot;
}
void dfs(int x){
v[x]=1;
for(int i=head[x];i;i=nxt(i)){
if(v[to(i)]) continue;
fa[to(i)]=x;
d[to(i)]=d[x]+dis(i);
fs= d[to(i)]>d[fs] ? to(i) : fs;
dfs(to(i));
}
v[x]=0;
}
inline void getd(){
fs=1;
dfs(1);
d[fs]=0;memset(fa,0,sizeof(fa));
int fs1=fs;fs=1;
dfs(fs1);memset(v,0,sizeof(v));
while(fs!=fs1){
v[fs]=1;
a[++cnt]=fs;
fs=fa[fs];
}
v[fs1]=1;a[++cnt]=fs1;
}
void getfr(int x){
v[x]=1;
for(int i=head[x];i;i=nxt(i)){
if(v[to(i)]) continue;
getfr(to(i));
fr[x]=max(fr[x],fr[to(i)]+dis(i));
}
}
int main()
{
scanf("%d%d",&n,&s);
for(int x,y,z,i=1;i<n;i++){
scanf("%d%d%d",&x,&y,&z);add(x,y,z);add(y,x,z);
}
getd();
for(int i=1;i<=cnt;i++) getfr(a[i]);
int ans=0x7ffffff,maxfr=0;
for(int i=1;i<=cnt;i++) maxfr=max(maxfr,fr[a[i]]);
for(int j=cnt,i=cnt;i;i--){
while(j && d[a[j]]-d[a[i]] <= s) j--;
ans=min(ans,max(maxfr,max(d[a[i]],d[a[1]]-d[a[j+1]])));
}
cout<<ans<<endl;
return 0;
}