藍書(演算法競賽進階指南)刷題記錄——BZOJ1999 樹網的核(樹的直徑+單調性優化)
阿新 • • 發佈:2018-12-17
題目大意:定義一條路徑到一個點的距離為一條路徑中距離這個點最近的一個點到這個點的距離,一條路徑的偏心距為一個距離這條路徑距離最遠的點到這條路徑的距離.給定一棵樹,求在這棵樹的一條直徑上的兩個點連成一條路徑F,使得F的長度小於s且偏心距最短.
我們首先可以從題目上得出,任意一條直徑的最小偏心距相等.
所以我們考慮取出一條直徑後,暴力列舉直徑上的核的兩個端點,將核上的點打上標記,將沒有標記的點暴力遍歷一遍得到核的偏心距去最小即可,時間複雜度.
考慮優化這個過程,發現確定一個端點,另一個端點一定是越遠越好,所以我們就可以直接確定另一個端點,繼續暴力列舉即可做到.
考慮繼續優化,將一條直徑上從點
其中表示從點i出發不經過直徑上任意一點的最短距離.由於直徑的最長性,我們可以得到絕對不會有任意一條經過直徑上任意一條邊的路徑比上式長.
那麼我們可以用單調佇列維護來達到解決這個問題.
但是上式一定可以轉化為.原因由於直徑是最長的,所以一定比小,一定比小.
那麼我們就可以直接得出,然後列舉i,直接確定j,並得出和即可解決本題.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; const int N=500000,INF=(1<<30)-1; struct side{ int y,next,v; }e[N*2+9]; int n,s,top=1,lin[N+9]; int use[N+9],pre[N+9],dis[N+9]; int u[N+9],tu,dp[N+9],ans,maxx; queue<int> q; void ins(int x,int y,int v){ e[++top].y=y;e[top].v=v; e[top].next=lin[x]; lin[x]=top; } int bfs(int st){ int maxv=st; for (int i=1;i<=n;i++) pre[i]=0,dis[i]=INF; q.push(st);dis[st]=0; while (!q.empty()){ int t=q.front();q.pop(); for (int i=lin[t];i;i=e[i].next) if (dis[e[i].y]==INF){ dis[e[i].y]=dis[t]+e[i].v; pre[e[i].y]=i; q.push(e[i].y); if (dis[maxv]<dis[e[i].y]) maxv=e[i].y; } } return maxv; } void diameter(){ u[tu=1]=bfs(bfs(1)); for (int i=pre[u[1]];i;i=pre[e[i^1].y]) u[++tu]=e[i^1].y; } void dfs(int k){ dp[k]=0;use[k]=1; for (int i=lin[k];i;i=e[i].next) if (!use[e[i].y]){ dfs(e[i].y); dp[k]=max(dp[k],dp[e[i].y]+e[i].v); } } Abigail into(){ scanf("%d%d",&n,&s); int x,y,v; for (int i=1;i<n;i++){ scanf("%d%d%d",&x,&y,&v); ins(x,y,v);ins(y,x,v); } } Abigail work(){ diameter(); for (int i=1;i<=n;i++) use[i]=0; for (int i=1;i<=tu;i++){ use[u[i]]=use[u[tu-i+1]]=1; if (i<=tu-i+1) swap(u[i],u[tu-i+1]); } int j=1; ans=INF; for (int i=1;i<=tu;i++) dfs(u[i]),maxx=max(maxx,dp[u[i]]); for (int i=1;i<=tu;i++){ while (j<=tu&&dis[u[j]]-dis[u[i]]<=s) j++; ans=min(ans,max(maxx,max(dis[u[i]],dis[u[tu]]-dis[u[j-1]]))); } } Abigail outo(){ printf("%d\n",ans); } int main(){ into(); work(); outo(); int _=0; return 0>_<0; }