1. 程式人生 > >E. Sonya and Ice Cream【樹的直徑+單調佇列】

E. Sonya and Ice Cream【樹的直徑+單調佇列】

題意:n個節點的樹,選k個連續的一條路上的頂點,最小化最大距離,最大距離的定義為:max(其他點到這k個點的距離) [還有一個名字叫偏心距]

思路:

k個點必定在樹的直徑上,證明:至少有1個點會在直徑上,接下來選1個點,相鄰的直徑點還是其他分支的點?那麼必定是相鄰的直徑點,因為右邊部分的直徑仍然是右邊樹的直徑。那麼很明顯要去列舉連續的k個子區間,用單調佇列滑動視窗模擬,答案是直徑兩個端點到k個連續區間的兩個端點,還有以k-2個點為根節點的最大距離。

樹的直徑,2遍dfs

列舉k個連續區間最大值,單調佇列

#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;
}