Description
公元 2044 年,人類進入了宇宙紀元。
L 國有 n 個星球,還有 n−1 條雙向航道,每條航道建立在兩個星球之間,這 n−1 條航道連通了 L 國的所有星球。
小 P 掌管一家物流公司, 該公司有很多個運輸計劃,每個運輸計劃形如:有一艘物流飛船需要從 ui 號星球沿最快的宇航路徑飛行到 vi 號星球去。顯然,飛船駛過一條航道是需要時間的,對於航道 j,任意飛船駛過它所花費的時間爲 tj,並且任意兩艘飛船之間不會產生任何干擾。
爲了鼓勵科技創新, L 國國王同意小 P 的物流公司參與 L 國的航道建設,即允許小P 把某一條航道改造成蟲洞,飛船駛過蟲洞不消耗時間。
在蟲洞的建設完成前小 P 的物流公司就預接了 m 個運輸計劃。在蟲洞建設完成後,這 m 個運輸計劃會同時開始,所有飛船一起出發。當這 m 個運輸計劃都完成時,小 P 的物流公司的階段性工作就完成了。
如果小 P 可以自由選擇將哪一條航道改造成蟲洞, 試求出小 P 的物流公司完成階段性工作所需要的最短時間是多少?
Input Format
第一行包括兩個正整數 n,m,表示 L 國中星球的數量及小 P 公司預接的運輸計劃的數量,星球從 1 到 n 編號。
接下來 n−1 行描述航道的建設情況,其中第 i 行包含三個整數 ai,bi 和 ti,表示第 i 條雙向航道修建在 ai 與 bi 兩個星球之間,任意飛船駛過它所花費的時間爲 ti。數據保證 1≤ai,bi≤n 且 0≤ti≤1000。
接下來 m 行描述運輸計劃的情況,其中第 j 行包含兩個正整數 uj 和 vj,表示第 j 個運輸計劃是從 uj 號星球飛往 vj號星球。數據保證 1≤ui,vi≤n
Output Format
輸出文件只包含一個整數,表示小 P 的物流公司完成階段性工作所需要的最短時間。
Sample Input
6 3 1 2 3 1 6 4 3 1 7 4 3 6 3 5 5 3 6 2 5 4 5
Sample Output
11
Hint
將第 1 條航道改造成蟲洞: 則三個計劃耗時分別爲:11,12,11,故需要花費的時間爲 12。
將第 2 條航道改造成蟲洞: 則三個計劃耗時分別爲:7,15,11,故需要花費的時間爲 15。
將第 3 條航道改造成蟲洞: 則三個計劃耗時分別爲:4,8,11,故需要花費的時間爲 11。
將第 4 條航道改造成蟲洞: 則三個計劃耗時分別爲:11,15,5,故需要花費的時間爲 15。
將第 5 條航道改造成蟲洞: 則三個計劃耗時分別爲:11,10,6,故需要花費的時間爲 11。
故將第 3 條或第 5 條航道改造成蟲洞均可使得完成階段性工作的耗時最短,需要花費的時間爲 11。
【題解】
二分答案+在線lca
由於答案很難直接算出來運用二分答案將問題轉化爲可行性
若經改造之後m條路徑的長度都小於ans則ans可行
接下來考慮如何算每條路徑長度及刪除哪條邊
求路徑可以在樹上倍增求lca時實現dist=dist[u]+dist[v]-2*dist[lca(u,v)](不會lca歡迎觀看本博客其他lca文章中的詳細介紹)(我這題是用離線lca tarjan大家可以自行百度)
假設未改造之前有k條路徑長度大於ans那麼肯定是刪除被k條路徑都經過的最長邊最優
首先考慮如何找到被k條路徑都經過的邊 可以用差分打標記的方法 在u,v節點各打上1,在lca(u,v)打上-1 用dfs遍歷一遍(樹型動規)
然後把所有經過k次的邊挑出來,排序一下找最長邊,這條邊就是刪除的邊,若最大路徑長-刪除邊邊長<=ans,ans就可行
詳見代碼(好像有點冗餘部分)
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
#include <ctime>
#include <cmath>
using namespace std;
int firstr[300005],firstq[300005],lca[300005],rec[300005],vis[300005],fa[300005],numr,numq;
int dis[300005],f[600005],a[300005];
int i,j,k,l,m,n,r,mid,ans,x,y,z;
struct info
{
int fr,ar,l,next;
}road[600005],ques[600005];
void add(int x,int y,int z)
{
numr++;road[numr]=(info){x,y,z,firstr[x]};firstr[x]=numr;
}
void add1(int x,int y,int z)
{
numq++;ques[numq]=(info){x,y,z,firstq[x]};firstq[x]=numq;
}
int getfa(int x)
{
if (fa[x]==x) return x;else return fa[x]=getfa(fa[x]);
}
void tarjan(int u)
{
int i,v;
vis[u]=1;fa[u]=u;
for (i=firstq[u];i;i=ques[i].next)
{
v=ques[i].ar;
if (vis[v])
{
lca[ques[i].l]=getfa(v);
rec[ques[i].l]=dis[u]+dis[v]-2*dis[lca[ques[i].l]];
}
}
for (i=firstr[u];i;i=road[i].next)
{
v=road[i].ar;
if (!vis[v])
{
dis[v]=dis[u]+road[i].l;
tarjan(v);
fa[getfa(v)]=u;
}
}
}
void dfs(int u,int fa)
{
int i;
for (i=firstr[u];i;i=road[i].next)
if (road[i].ar!=fa)
{
dfs(road[i].ar,u);
a[u]+=a[road[i].ar];
f[i]=a[road[i].ar];
}
}
bool pd(int l)
{
int i,j,k,mx;
memset(a,0,sizeof(a));
memset(f,0,sizeof(f));
for (i=1,k=0;i<=m;i++)
if (rec[i]>l)
{
k++;a[ques[2*i].fr]++;a[ques[2*i].ar]++;a[lca[i]]-=2;
}
dfs(1,-1);
for (i=1,mx=0;i<=numr;i++)
if (f[i]==k) mx=max(mx,road[i].l);
for (i=1;i<=m;i++) if (rec[i]-mx>l) return 0;
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for (i=1;i<n;i++)
scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
for (i=1;i<=m;i++)
scanf("%d%d",&x,&y),add1(x,y,i),add1(y,x,i);
tarjan(1);
for (i=1;i<=m;i++) r=max(r,rec[i]);
for (;l<=r;)
{
mid=(l+r)/2;
if (pd(mid)) r=mid-1,ans=mid;else l=mid+1;
}
printf("%d",ans);
}