Link:https://www.lydsy.com/JudgeOnline/problem.php?id=2282
Solution:
看到球最大值最小 ------> 想到二分答案
首先要推导出一些性质:
1、这条路径一定在树的直径上
感性证明:直径上的每个点通向直径末端的路径都是这个方向从该点出发的最长距离,
于是如果将答案路径引上直径的分支必定不会使答案更优
2、设非直径点到直径的最长距离为$len_{not}$,选取的路径到直径两端的距离分别为$len_{on1}$,$len_{on2}$
则该路径的答案为$max(len_{not},len_{on1},len_{on2})$
感性证明:如果路径长度达不到直径,同上述直径上点的性质,则新增的最远点只可能为直径的两端
接下来先预处理$len_{not}$,以其为下界在直径上二分两端缩减的距离
注意:此题结果只能为简单路径,因此如果$s>len_{直径}$,则输出$len_{not}$即可
要仔细审题啊……
Code:
//by NewErA #include <bits/stdc++.h> using namespace std; typedef pair<int,int> P; const int MAXN=3e5+5; int n,s,d[MAXN],dia[MAXN],f[MAXN],rt1,rt2,cnt=0,D; bool on_dia[MAXN]; vector<P> a[MAXN]; void bfs(int st) { memset(d,-1,sizeof(d)); d[st]=0;queue<int> q;q.push(st); while(!q.empty()) { int cur=q.front();q.pop(); for(int i=0;i<a[cur].size();i++) { P t=a[cur][i]; if(d[t.first]==-1) { if(on_dia[t.first]) d[t.first]=d[cur]; else d[t.first]=t.second+d[cur]; q.push(t.first);f[t.first]=cur; } } } } bool check(int k) { int l=1,r=cnt; while(dia[1]-dia[l+1]<=k && l<cnt) l++; while(dia[r-1]<=k && r>1) r--; return dia[l]-dia[r]<=s; } int main() { cin >> n >> s; for(int i=1;i<n;i++) { int x,y,z;cin >> x >> y >> z; a[x].push_back(P(y,z));a[y].push_back(P(x,z)); } bfs(1);for(int i=1;i<=n;i++) if(d[i]>d[rt1]) rt1=i; bfs(rt1);for(int i=1;i<=n;i++) if(d[i]>d[rt2]) rt2=i; //求直径 D=d[rt2]; dia[++cnt]=d[rt2];on_dia[rt2]=true; while(rt2!=rt1) { rt2=f[rt2]; dia[++cnt]=d[rt2];on_dia[rt2]=true; } bfs(rt2); //处理出直径上的点,将其的d值设为0 int l=0,r=D; for(int i=1;i<=n;i++) l=max(l,d[i]); if(s<D) while(l<=r) { int mid=(l+r)>>1; if(check(mid)) r=mid-1; else l=mid+1; } cout << l; return 0; }
Review:
1、最大值最小类的问题,使用二分答案来求解
2、还是要注意审题啊,题目求的是简单路径