題目傳送門
題目大意:有一顆 n
nn 個點的樹,每條邊有乙個權值(長度),有 m
mm 條路線,每條路線的長度為路線上的所有邊的長度之和,你可以將任意一條邊的長度變成 0
00,改完之後,要使所有路徑中最長的路徑盡可能短,問最短可以是多少。
比較顯然的,改的這條邊肯定在 m
mm 條路線中最長的那條中。
假如現在有兩條路徑,長度為 1000
1000
1000
和 999
99999
9,有一條邊長度為 500
50050
0,在路徑 1
11 上,有一條邊長度為 2
22,同時在路徑 1
11 和路徑 2
22 上,你選擇將哪一條邊的權值改為 000?
答案很明顯——將長度為 2
22 的那條邊的長度改為 000。
假如問題變成這樣:兩條路徑,長度 1000
1000
1000
和 500
50050
0,一條長度為 500
50050
0 的邊在路徑 1
11 上,一條長度為 2
22 的邊在兩條路徑上,這個時候你會選誰?
答案也很明顯——將長度為 500
50050
0 的邊變成 000。
推廣一下上面的問題,得到結論:
於是可以考慮二分,二分最長的邊的長度,設當前長度為 mid
midmi
d,如果所有路徑的長度都小於等於 mid
midmi
d,那麼 r=m
id−1
r=mid-1
r=mid−
1,否則判斷一下長度大於 mid
midmi
d 的路徑是否可以通過 將一條邊的長度變成 0
00 後長度都變得小於等於 mid
midmi
d,可以的話 r=m
id−1
r=mid-1
r=mid−
1 否則 l=m
id+1
l=mid+1
l=mid+
1(……有點囉嗦),偽**長這個樣子:
int ans;
while
(l<=r)
}
二分的單調性的證明
設有兩個數 x
xx 和 y(x
y(xy( x。如果長度大於 x xx 的邊 在 一條邊長度變成 0 00 後 長度都小於等於 x xx,那麼,同樣可以通過刪除這條邊,使得所有的邊的長度都小於 yyy。 證畢剩下的問題,就是如何判斷長度大於 mid midmi d 的路徑,在一條邊長度變成 0 00 後,是否可以使得這些邊長度都小於 mid midmi d。顯然,我們就是要找出所有的 被這些路徑中的每一條都覆蓋到的 邊 中的 最長的 那一條。那怎麼判斷每一條邊有沒有被所有路徑覆蓋呢? 對於每一條路徑,將路徑上的所有邊 +1+1 +1,最後統計一下所有的邊,看看有沒有邊的值為路徑總數,有的話用它的長度更新一下 max maxma x 即可。 顯然,「將路徑上的所有邊+1」這一操作,可以用樹上差分搞。 找到最長的那一條邊之後,用最長的路徑減去這條邊的長度,判斷是否小於等於 mid midmi d,如果是的話,那麼其他的路徑減去這條邊也一定小於等於 mid midmi d,然後 r=m id−1 r=mid-1 r=mid− 1 即可,因為答案要盡可能小。 **如下:#include
#include
#include
using
namespace std;
int n,m,len=0;
struct node
;node e[
600010];
int first[
300010];
int deep[
300010];
int a[
300010];
int f[
300010][
20];int dis[
300010
],disfa[
300010];
//dis用來記錄該點到root的距離,disfa記錄該點到父親的距離
void
dfs_getfa
(int x,
int fa)
}void
buildroad
(int x,
int y,
int z)
int l=
0,r=0;
struct nod
;nod lu[
300010];
void
swap
(int
&x,int
&y)int
lca(
int x,
int y)
}int tot=18;
while
(x!=y)
return x;
}bool
cmp(nod x,nod y)
int big,mid,d;
void
dfs_getans
(int x)
//統計覆蓋情況
if(a[x]
==d&&disfa[x]
>big)big=disfa[x]
;//如果該邊被覆蓋次數等於路徑數就更新一下big(就是上面說的max)
}int
main()
deep[1]
=1;dis[1]
=0;dfs_getfa(1
,0);
for(
int j=
1;j<=
18;j++
)for
(int i=
1;i<=n;i++
) f[i]
[j]=f[f[i]
[j-1]]
[j-1];
for(
int i=
1;i<=m;i++
)scanf
("%d %d"
,&lu[i]
.x,&lu[i]
.y);
for(
int i=
1;i<=m;i++
) lu[i]
.z=dis[lu[i]
.x]+dis[lu[i]
.y]-
2*dis[
lca(lu[i]
.x,lu[i]
.y)]
;//lu[i].z表示該路徑的長度,如果不知道為什麼可以這樣求可以參考**下面的注釋
sort
(lu+
1,lu+m+
1,cmp)
;//將路徑按長度排序
r=lu[1]
.z;int ans;
l=max(
0,r-
1000);
//因為最長的邊長度不超過1000,所以最後的那條最長的路徑最多比現在的最長的路徑短1000
//還有,不加這個優化會t掉乙個點(各種 卡常+優化 都過不去)
while
(l<=r)
if(d==
0)ans=mid,r=mid-1;
else
big=0;
dfs_getans(1
);if(lu[1]
.z-big<=mid)ans=mid,r=mid-1;
else l=mid+1;
}}printf
("%d"
,ans)
;}
NOIP 提高組2015 運輸計畫
題意 n個點的樹,m條鏈,求將一條邊的權值置為0使得最大鏈長最小。演算法 二分 樹上差分 題解 最大值最小化問題,先考慮二分最大鏈長。對所有鏈長 mid的鏈整體 1 樹上差分 然後掃一遍,對 在所有不滿足鏈上 的邊取最大值並check。具體做法 對於二分的最大鏈長,將所有鏈長 mid的鏈取最大值 鏈...
NOIP2015提高組 運輸計畫
題目 bzoj4326 洛谷p2680 vijos p1983 uoj 150 codevs4632 codevs5440。題目大意 有一棵帶權樹,有一些運輸計畫,第i個運輸計畫從ai到bi,耗時為ai到bi的距離,所有運輸計畫一起開始。現在可以把一條邊權變成0,求最終運輸計畫最短要多少時間。解題思...
NOIP2015提高組Day2 運輸計畫
其實題目說那麼多,一句話就是 給定一棵帶權樹與m mm條路徑,你可以使一條樹上的邊的權值變為0,問你m mm條路徑的長度的最大值最小是多少。這道題讓我想到了貨車運輸這道題,但是更難,但方法可以借鑑。因為這是最大值最小問題,很顯然可以二分答案。那這個二分判斷怎麼打呢?我們如果遇到某條邊,所有超時的邊 ...