E. Sonya and Ice Cream【樹的直徑+單調佇列】
阿新 • • 發佈:2019-02-04
題意: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; }