题意:n个节点的树,选k个连续的一条路上的顶点,最小化最大距离,最大距离的定义为:max(其他点到这k个点的距离) [还有一个名字叫偏心距]
思路:
k个点必定在树的直径上,证明:至少有1个点会在直径上,接下来选1个点,相邻的直径点还是其他分支的点?那么必定是相邻的直径点,因为右边部分的直径仍然是右边树的直径。那么很明显要去枚举连续的k个子区间,用单调队列滑动窗口模拟,答案是直径两个端点到k个连续区间的两个端点,还有以k-2个点为根节点的最大距离。
树的直径,2遍dfs
枚举k个连续区间最大值,单调队列
题解参考来自CSL'blog
#include<bits/stdc++.h>
#define PI acos(-1.0)
#define pb push_back
#define F first
#define S second
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int MOD=1e9+7;
ll a[N],sum[N],dis[N];
int par[N];
int vis[N];
vector<pair<ll,ll> > edge[N];
vector<ll> bet;
vector<ll> maxsubtree;
vector<ll> diameter;
ll maxlen=0,n,k,st,ed;
void dfs(int u,int f){
par[u]=f;
for(auto t:edge[u]){
ll v=t.F,w=t.S;
if(v==f||vis[v]==1) continue;
dis[v]=dis[u]+w;
maxlen=max(maxlen,dis[v]);
dfs(v,u);
}
}
void FindDiameter(){
memset(dis,0,sizeof dis);
dfs(1,-1);
ll mx=-1;
for(int i=1;i<=n;i++)
if(dis[i]>mx) mx=dis[i],st=i;
memset(dis,0,sizeof dis);
dfs(st,-1);
mx=-1;
for(int i=1;i<=n;i++)
if(dis[i]>mx) mx=dis[i],ed=i;
for(int i=ed;i!=-1;i=par[i]) diameter.pb(i);
reverse(diameter.begin(),diameter.end());///find the diameter of the tree
// for(auto t : diameter) cout <<t <<" ";
// cout <<"*****"<<"\n";
return ;
}
void FindDisBetweendpoints(){
for(auto t:diameter) bet.pb(dis[t])/*,cout << dis[st]<<"! ";cout <<"\n"*/;
// reverse(bet.begin(),bet.end());
// for(auto t:bet) cout <<t <<" ";
// cout <<"*****"<<"\n";
return ;
}
void FindMaxlenToSubtree(){
k=min(k,(ll)diameter.size());/// in order to fit situations,all points are on diameter of the tree
for(auto u:diameter) vis[u]=1;
for(auto u:diameter){
maxlen=0;
dis[u]=0;
dfs(u,-1);
maxsubtree.pb(maxlen);
}
return ;
}
int main(void){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i=1;i<=n-1;i++){
ll u,v,w;
cin>>u>>v>>w;
edge[u].pb({v,w});
edge[v].pb({u,w});
}
FindDiameter();
FindDisBetweendpoints();
FindMaxlenToSubtree();
deque<pair<ll,ll> > q;
ll sz=(ll)diameter.size();
ll ans=1e17;
for(int i=0,j=0;i<=sz-k;i++){
while(j<=i+k-1){
while(!q.empty()&&maxsubtree[j]>q.back().F) q.pop_back();
q.push_back({maxsubtree[j],j}),j++;
}
while(q.front().S<i) q.pop_front();
ans=min(ans, max({q.front().F,bet[i],bet[(ll)bet.size()-1]-bet[i+k-1]}) );
// cout << (ll)q.front().F <<" "<<bet[i]<<" ~"<<bet[(int)bet.size()-1]-bet[i+k-1]<<endl;
}
cout << ans << endl;
return 0;
}