题解 P2491 【[SDOI2011]消防】

安好

2017-10-26 17:11:10

Solution

/\* 20%的数据,就是NOIP那道树网的核。 100%这么大的数据范围,若直径长度为d,dlogd可能会被卡 先bfs两遍求出树的直径(防爆栈),O(n)的 然后维护 g[i]表示i是路径右端点时,右边那段删掉的直径长度。 f[i]表示i是路径左端点时,左边那段删掉的直径长度。 h[i]表示i是直径上的点,每个直径上的点不是都有一棵(或者很多棵) 由非此直径上点组成的树(森林)嘛,点i到这些子节点中最远的那个的距离。 然后在这个序列上跑双指针。就是路径长度不是有限制嘛,然后从左到右枚举左端点,然后右端点是非严格单调右移的。 时间复杂度线性。而对于一段路径区间[l,r],它作为枢纽时的答案为 max(max(f[l],g[r]),max{hi,i∈[l,r]}) 然后最右边那个怎么搞呢? 单调队列,维护hi最大值啊~ \*/ ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define N 3000007 #define inf 0x3f3f3f3f using namespace std; int n,m,k,s,t,ans,cnt,num,L,R; int head[N],f[N],g[N],h[N],fa[N],vis[N]; int belong[N],tmp[N],que[N],id[N]; struct edge{ int v,net,w; }e[N<<1]; queue<int>q; inline int read() { int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } inline void add(int u,int v,int w) { e[++cnt].v=v;e[cnt].w=w,e[cnt].net=head[u];head[u]=cnt; } int bfs(int s,int Time) { int len=0;q.push(s); g[s]=0,fa[s]=s,vis[s]=Time; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].net) { int v=e[i].v; if(vis[v]==Time) continue; g[v]=g[u]+e[i].w;len=max(len,g[v]); fa[v]=u;vis[v]=Time; q.push(v); } }return len; } void solve() { int i; for(s=1,bfs(s,++num),i=1;i<=n;i++) if(g[i]>g[s]) s=i; for(bfs(s,++num),i=1;i<=n;i++) if(g[i]>g[t]) t=i; belong[n=1]=t;num++; do{ t=fa[t],belong[++n]=t,vis[t]=num; }while(t!=s); for(i=1;i<=n;i++) f[i]=g[belong[1]]-g[belong[i]]; for(i=1;i<=n;i++) tmp[i]=g[belong[i]]; for(i=1;i<=n;i++) h[i]=bfs(belong[i],num); for(i=1;i< n;i++) g[i]=tmp[i];g[n]=0; } int main() { int x,y,z; n=read();m=read(); for(int i=1;i<n;i++) { x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } solve(); int l=1,r=0;ans=inf; for(l=1;l<=n;l++) { while(r<n && f[r+1]-f[l]<=m) { ++r;while(L<=R && que[R]<h[r]) R--; que[++R]=h[r];id[R]=r; } ans=min(ans,max(max(f[l],g[r]),que[L])); if(id[L]<=l)L++; } printf("%d\n",ans); return 0; } ```