Luogu2680 NOIp2015 運輸計畫

2022-04-28 19:33:08 字數 1523 閱讀 2150

傳送門

$sol$

最暴力的做法就是列舉最長鏈上的邊,然後再算一次所有的鏈長,更新$ans$.

這裡要求最大的最小,容易想到二分答案.對於二分的值$mid$,掃一遍所有的鏈,若鏈長小於等於$mid$,那麼是合法的不需要處理的.否則,就記錄鏈上所有的邊經過的次數$+1$.最後找到被經過次數等於鏈長大於$mid$的鏈數的最大的邊,改成0,再看最長鏈是否小於等於$mid.$如果沒有這樣的邊,那麼$mid$顯然是不合法的,是偏小的.

維護邊經過的次數樹上差分.

$code$

#include#define il inline

#define ri register int

#define go(i,a,b) for(ri i=a;i<=b;++i)

#define yes(i,a,b) for(ri i=a;i>=b;--i)

#define e(i,u) for(ri i=b[u];i;i=a[i].nt)

#define mem(a,b) memset(a,b,sizeof(a))

#define ll long long

#define db double

#define inf 2147483647

using

namespace

std;

il int

read()

while(c>='

0'&&c<='9')

return x*y;

}const

int n=300010

;int n,m,b[n],ct,dep[n],f[n][19],dis[n],t[n],num,dd,as

;bool

fl;struct nda[n*2

];struct nd1c[n];

il void add(ri u,ri v,ri w);b[u]=ct;}

il void

build(ri u,ri fa)

}il

intlca(ri u,ri v)

il bool cmp(nd1 x,nd1 y)

il int

dfs(ri u)

ret+=t[u];

return

ret;

}il

bool

ck(ri x)

dfs(1);

if(!fl)return0;

if(c[1].l-dd>x)return0;

return1;

}int

main()

build(

1,0);

go(i,

1,m);}

sort(c+1,c+n+1

,cmp);

ri l=0,r=c[1].l+1

;

while(l<=r)

printf(

"%d\n

",as

);

return0;

}

view code

luogu2680 運輸計畫

很明顯,由於是求最長路的最小值,我們可以使用二分求解.我們二分乙個長度 mid 將所有使得 dis u,v 大於 mid 的點對 u,v 找出,設總共有 m 條這樣的邊,那麼我們需要改變的改變邊一定是被這 m 條邊都經過的邊,所以我們只需要找到滿足這個要求的長度最大的改變邊使得這 m 條邊中最長的一...

Luogu2680 運輸計畫

我們對每條邊 i 單獨考慮,那麼設 v i 為這條邊的長度,經過它的路徑長度集合為 s 未經過它的路徑長度集合為 t ans i max begin s v i t end ans min ans i 對於 s 我們直接對於一條長度為 l 的路徑,該路徑上的邊都對 l 取 max 用樹鏈剖分很容易處...

luogu2680 運輸計畫

首先二分列舉答案t,考慮刪去的邊應滿足的條件 所有大於 t的鏈全部經過且長度不小於最長鏈 t,第二個條件很好判斷,考慮第乙個條件。先考慮有祖先 後代關係的鏈,用如果把這條鏈到 1看成乙個序列,那麼差分一下再 dfs一遍就可以對這條鏈上每乙個點打上標記。然後沒有祖先 後代關係的鏈,同樣可以分解成兩條鏈...