【Luogu】P2491消防(单调队列)
首先可以想到路径一定是在直径上的。
然后对每个点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; }