[题解]树网的核

题意:给一棵无根树,要求一个有长度限制的路径使得 距离这条路径最远的点 距此路径的距离最小

性质:对一个点,距离它最远的点是直径上两个端点之一,不然不要原来的端点把那个更远的点连到直径上直径会更长

对直径上的一条路径,如果某 不在直径上的点b 到此路径上最近的点 是这条路径的端点a,那么这个距离一定小于 a到距a最远的直径端点,

如果b到此路径上最近的点不是路径端点而是路径内点c,那么这个点b就需要被考虑进答案中了,因为c虽然到直径某端点很远但是因为它不是路径的端点,所以实际上这个路径距离直径的某端点会稍短一些,这样点b就有可能更新答案,例:(d为直径一端点

所以我们把直径看做一个序列,每个点都有一个权值即距它最远的点的距离,现在要在序列上取不大于s的一段使最大权值最小,我们可以在直径上尺取,用单调队列维护最大值,注意还要处理路径端点到直径端点

然而其实不用单调队列,直接对所有点的权值取max即可,原因在于如果某点不在尺取范围内,那么它一定不如到直径的端点长(上面已证,如果答案取上这个点那么我们取max更新了很对,如果没取上那么一定有其他大于它的,对答案没有影响

#include<bits/stdc++.h>
using namespace std;
const int maxn=309;
int n,s;
struct node{
    int v,nxt,w;
}e[maxn<<1];
int head[maxn],cnt;
inline void add(int u,int v,int w){
    e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;
}
int dis[maxn],far,from[maxn],fa[maxn],dia[maxn];
void pre(int x,int f){
    fa[x]=f;
    for(int i=head[x];i;i=e[i].nxt){
        if(e[i].v==f||dia[e[i].v])continue;
        dis[e[i].v]=dis[x]+e[i].w;
        pre(e[i].v,x);
    }
    if(dis[x]>dis[far])far=x;
}
int ans=1e9;
int main(){
    scanf("%d%d",&n,&s);
    for(int i=1,u,v,w;i<n;i++){
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);add(v,u,w);
    }
    dis[1]=0;pre(1,0);
    dis[far]=0;pre(far,0);
    int top=far;
    for(int i=top,j=top;i;i=fa[i]){
        while(dis[j]-dis[i]>s)j=fa[j];//³ßÈ¡ 
        int x=max(dis[top]-dis[j],dis[i]);//·¾¶¶Ëµãµ½Ö±¾¶¶ËµãµÄ×îС¹±Ï×
        ans=min(ans,x);
    }
    for(int i=top;i;i=fa[i])dia[i]=1;//±ê¼ÇÖ±¾¶ 
    for(int i=top;i;i=fa[i]){
        far=i;dis[far]=0;
        pre(i,fa[i]);//¼ÆËãÿ¸öµãȨֵ 
    }
    for(int i=1;i<=n;i++)ans=max(ans,dis[i]);
    printf("%d",ans);
}

 

posted @ 2019-10-15 10:10  羊肉汤泡煎饼  阅读(115)  评论(0编辑  收藏  举报