【Luogu P2491】[SDOI2011]消防(树的直径)

题目链接

题目描述

某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000)。
这个国家的人对火焰有超越宇宙的热情,所以这个国家最兴旺的行业是消防业。由于政府对国民的热情忍无可忍(大量的消防经费开销)可是却又无可奈何(总统竞选的国民支持率),所以只能想尽方法提高消防能力。
现在这个国家的经费足以在一条边长度和不超过s的路径(两端都是城市)上建立消防枢纽,为了尽量提高枢纽的利用率,要求其他所有城市到这条路径的距离的最大值最小。
你受命监管这个项目,你当然需要知道应该把枢纽建立在什么位置上。

题解

跟据树的直径的性质(树网的核那道题的体面):树上一个点的最远点是树的直径的一个端点,我们可以得出,这条路径一定在树的直径上。而且每一条直径的答案是相同的。
所以我们先随便抠一条直径出来。
对于选出来的一段路径,我们要求出其他不在该路径上的到该路径上点的最大距离。
那么其实就是对于在直径上的点,求出以它为起点的不过直径上点的最大路径长度记为 val[u] v a l [ u ]
为什么不用管直径上没被选到的点? 还是因为直径的性质,因为选出的是直径上连续的点,那么选出路径的端点到离它较近端的直径端点的距离,为该点出发经过未选的直径上的点和未经过直径上的点的最长路径。
因此我们把 val[u] v a l [ u ] 算出来,尺取法选择路径端点,用一个单调队列维护一下就可以了。

#include
#include
#include
#include
#include
#include
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();int t=1;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=-1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    return x*t;
}
const int N=3e5+100;
struct edge{
    int to;int next;int w;
}a[N<<1];
bool vis[N];
int head[N];int cnt;int pre[N];
int st[N];int Dis[N];int val[N];
int top=0;
int Q[N];int id[N];
int h,t;
int D;int P;
inline void add(int x,int y,int w)
{
    a[++cnt]=(edge){y,head[x],w};
    head[x]=cnt;
}
int n,S;
inline void dfs1(int u,int fa,int dis)
{if(dis>D) D=dis,P=u;for(register int v,i=head[u];i;i=a[i].next){v=a[i].to;if(v==fa) continue;dfs1(v,u,dis+a[i].w);}}
inline void dfs2(int u,int fa,int dis)
{
    Dis[u]=dis;if(dis>D) D=dis,P=u;
    for(register int v,i=head[u];i;i=a[i].next){v=a[i].to;if(v==fa) continue;pre[v]=u;dfs2(v,u,dis+a[i].w);}
}
inline void dfs3(int u,int fa,int dis)
{
    D=max(D,dis);vis[u]=1;
    for(register int v,i=head[u];i;i=a[i].next)
    {
        v=a[i].to;if(v==fa||vis[v]) continue;
        dfs3(v,u,dis+a[i].w);
    }
}
inline void insert(int x,int p){while(t>=h&&x>=Q[t]) t--;Q[++t]=x;id[t]=p;}
int main()
{
    n=read();S=read();register int x,y,z;
    for(register int i=1;i1,0,0);D=0;
    dfs2(P,0,0);D=0;
    for(register int v=P;v;v=pre[v]) st[++top]=v,vis[v]=1;
    for(register int i=top;i;i--) D=0,dfs3(st[i],0,0),val[i]=D;
    register int L=1,R=1;h=0;t=0;register int ans=2147483647;
    while(L<=top){
        while(id[h]while(R<=top&&(Dis[st[L]]-Dis[st[R]])<=S) insert(val[R],R),R++;
        ans=min(ans,max(max(Dis[st[1]]-Dis[st[L]],Dis[st[R-1]]-Dis[st[top]]),Q[h]));
        L++;
    }
    printf("%d\n",ans);
}

你可能感兴趣的:(——图论———,树上问题,======题解======)