题意:一个树上建两个加油站。使得全部点到达其近期加油站的最大距离最小。
解法:二分答案。关键时二分时候,要最合理话布局两个点的位置,做法是处理出来树的直径,然后在直径两端分别向中间移动二分的x步的两个点布下加油站。
贪心能够证明正确性;
代码:
/****************************************************** * @author:xiefubao *******************************************************/ #pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <queue> #include <vector> #include <algorithm> #include <cmath> #include <map> #include <set> #include <stack> #include <string.h> //freopen ("in.txt" , "r" , stdin); using namespace std; #define eps 1e-8 #define zero(_) (_<=eps) const double pi=acos(-1.0); typedef long long LL; const int Max=200010; const LL INF=0x3FFFFFFF; vector<int> vec[Max]; int n; vector<int> diameter; int rem[Max]; int rem1[Max]; int rem2[Max]; int dis[Max]; int st,en; int dui[Max*2]; int now=0; int count1=10; void dfs(int u) { memset(rem,0,sizeof rem); rem[u]=-1; int left=0; int right=1; dui[0]=u; dis[u]=0; while(left<right) { for(int i=0; i<vec[dui[left]].size(); i++) { if(rem[vec[dui[left]][i]]==0) { rem[vec[dui[left]][i]]=dui[left]; dis[vec[dui[left]][i]]=dis[dui[left]]+1; dui[right++]=vec[dui[left]][i]; } } left++; } } void ran1(int u,int* re) { int left=0; int right=1; dui[0]=u; dis[u]=0; re[u]=count1; while(left<right) { if(dis[dui[left]]<now) for(int i=0; i<vec[dui[left]].size(); i++) { if(re[vec[dui[left]][i]]!=count1) { re[vec[dui[left]][i]]=count1; dis[vec[dui[left]][i]]=dis[dui[left]]+1; if(dis[vec[dui[left]][i]]<now) dui[right++]=vec[dui[left]][i]; } } left++; } } bool OK(int middle) { count1++; now=middle; ran1(diameter[middle],rem1); ran1(diameter[diameter.size()-1-middle],rem2); for(int i=1; i<=n; i++) { if(rem1[i]!=count1&&rem2[i]!=count1) return false; } return true; } int main() { int t; cin>>t; while(t--) { for(int i=1;i<Max;i++) vec[i].clear(); diameter.clear(); scanf("%d",&n); for(int i=0; i<n-1; i++) { int a,b; scanf("%d%d",&a,&b); vec[a].push_back(b); vec[b].push_back(a); } dfs(1); int d=0; for(int i=1; i<=n; i++) { if(dis[i]>d) { d=dis[i]; st=i; } } dfs(st); d=0; for(int i=1; i<=n; i++) { if(dis[i]>d) { d=dis[i]; en=i; } } while(en!=-1) { diameter.push_back(en); en=rem[en]; } int left=0,right=diameter.size(); while(left<=right) { int middle=(left+right)/2; if(!OK(middle)) { left=middle+1; } else { right=middle-1; } } int a=diameter[left]; int b=diameter[diameter.size()-1-left]; if(a==b) { a=b==n?n-1:b+1; } //cout<<diameter.size()<<" "<<OK(0)<<OK(1)<<endl; cout<<left<<" "<<a<<" "<<b<<' '; } return 0; } /* 34 4 1 2 1 3 1 4 5 1 2 2 3 3 4 4 5 3 1 2 2 3 */