【Luogu】P2491消防(单调队列)

时间:2021-02-06 18:43:08

  题目链接

  首先可以想到路径一定是在直径上的。

  然后对每个点dfs出不经过直径的以它开始的路径最大长度,记为dis

  然后就可以求出直径之后枚举左右端点,设左端点l右端点r,直径上点距离直径上起点的距离用sum[]表示

  则此时的$ans=max(max(sum[l],sum[End]-sum[r]),max(dis[i])l<=i<=r))$

  右面那个玩意可以单调队列维护。

  (然后我单调队列打反了变成维护最小值,GG。)

  

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cstring>
#include<cstdlib>
#define maxn 600030
using namespace std;
inline long long read(){
    long long num=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-')    f=-1;
        ch=getchar();
    }
    while(isdigit(ch)){
        num=num*10+ch-'0';
        ch=getchar();
    }
    return num*f;
}

struct Edge{
    int next,to,val;
}edge[maxn*2];
int head[maxn],num;
inline void add(int from,int to,int val){
    edge[++num]=(Edge){head[from],to,val};
    head[from]=num;
}

int dis[maxn];
bool vis[maxn];
int stack[maxn],top;
int sum[maxn];
int End;
int n,m;

void find(int x,int fa){
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa)    continue;
        dis[to]=dis[x]+edge[i].val;
        find(to,x);
        if(dis[End]<dis[to])    End=to;
    }
}

void record(int x,int fa){
    if(x==End){
        stack[++top]=x;
        vis[x]=1;
        return;
    }
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa)    continue;
        record(to,x);
        if(vis[to]){
            stack[++top]=x;
            sum[top]=sum[top-1]+edge[i].val;
            vis[x]=1;
        }
    }
}

void calc(){
    find(1,1);
    dis[End]=1;
    int Start=End;
    find(End,End);
    record(Start,Start);
}

int dfs(int x,int fa){
    int ans=0;
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa||vis[to]==1)    continue;
        ans=max(ans,dfs(to,x)+edge[i].val);
    }
    return ans;
}

int que[maxn],h=1,t;


int main(){
    n=read(),m=read();
    for(int i=1;i<n;++i){
        int from=read(),to=read(),val=read();
        add(from,to,val);
        add(to,from,val);
    }
    calc();
    for(int i=1;i<=top;++i)    dis[i]=dfs(stack[i],stack[i]);
    int rig=1;int ans=0x7fffffff;
    for(int i=1;i<=top;++i){
        int le=sum[i];
        while(sum[rig+1]-sum[i]<=m&&rig<top){
            rig++;
            while(h<=t&&dis[que[t]]<=dis[rig])    t--;
            que[++t]=rig;
            while(h<=t&&que[h]<i)    h++;
            int now=max(le,max(sum[top]-sum[rig],dis[que[h]]));
            ans=min(ans,now);
        }
    }
    printf("%d\n",ans);
    return 0;
}