E. Sonya and Ice Cream

题意: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;
}