D49 树的直径 两次DFS+双指针 P2491 [SDOI2011] 消防
D49 树的直径 P2491 [SDOI2011] 消防_哔哩哔哩_bilibili
P2491 [SDOI2011] 消防 - 洛谷
P1099 [NOIP 2007 提高组] 树网的核 - 洛谷 雷同
给定一棵树,选择一条长度不超过 s 的路径,使得路径外的点到该路径的最大距离最小。输出最大距离最小值。
思路
1. 两次 DFS,先求出一条直径,$pre$ 数组记录这条直径上的点,$l$ 记录直径的左端点,$d$ 数组记录各点到直径右端的距离。
2. 先在直径上搜答案:从左端点开始向右遍历直径,$i$ 指针在右,$j$ 指针在左,二者之间的距离满足 $≤ s$,取 $min(ans,max(d[i],d[l]-d[j]))$。
3. 对直径的点染色,用 col 数组记录,col 数组保证下一步只搜索支路。
4. 再在支路上搜答案:从左端点开始向右遍历直径,对直径上的每个点 dfs,求出以这个点 $i$ 为起点的支路最长链的长度 $d[p]$。取 $max(ans,d[p])$。
// 两次DFS+双指针 O(n) #include<bits/stdc++.h> using namespace std; const int N=300010; int n,s,p,l,d[N],pre[N],col[N]; vector<pair<int,int>> e[N]; void dfs(int u,int fa){ if(d[u]>d[p]) p=u; //记录最远点 pre[u]=fa; //记录路径 for(auto [v,w]:e[u]){ if(v==fa||col[v]) continue; d[v]=d[u]+w; dfs(v,u); } } int main(){ ios::sync_with_stdio(0); cin.tie(0); cin>>n>>s; for(int i=1,x,y,z;i<n;i++){ cin>>x>>y>>z; e[x].emplace_back(y,z); e[y].emplace_back(x,z); } dfs(1,0); d[p]=0; dfs(p,0); l=p; int ans=2e9; for(int i=l,j=l;i;i=pre[i]){ //直径上的答案 while(d[j]-d[i]>s) j=pre[j]; ans=min(ans,max(d[i],d[l]-d[j])); } for(int i=l;i;i=pre[i])col[i]=1; //直径染色 for(int i=l;i;i=pre[i]){ //直径外的答案 p=i; d[p]=0; dfs(i,pre[i]); ans=max(ans,d[p]); } printf("%d\n",ans); }
浙公网安备 33010602011771号