NOIp提高組 2015 運輸計畫

2021-08-30 08:41:59 字數 4024 閱讀 8875

題目傳送門

題目大意:有一顆 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條路徑的長度的最大值最小是多少。這道題讓我想到了貨車運輸這道題,但是更難,但方法可以借鑑。因為這是最大值最小問題,很顯然可以二分答案。那這個二分判斷怎麼打呢?我們如果遇到某條邊,所有超時的邊 ...