P1084 疫情控制(二分答案,倍增,貪心)

2021-09-29 06:31:15 字數 4524 閱讀 4853

p1084 疫情控制

給定一棵樹,一些點(不包括樹根)上有軍隊,每條邊有乙個邊權(移動時間),各個軍隊可以同時移動,求最小的移動時間,使得每條通往任意葉子結點的路徑上至少都有一支軍隊.

二分答案:

通過觀察題意可知,答案具有單調性(如果p

pp小時時能控制所有的邊境城市,那麼q(q

>p)

q(q>p)

q(q>p)

小時時同樣可以控制所有的邊境城市),所以我們可以二分答案.

貪心:

通過以上的二分答案,我們把乙個求值問題轉化成了判定性問題,接下來我們要做的就是考慮如何判斷我們當前二分出來的時間是否能夠成立.

我們可以發現,離根節點越近的節點,能夠控制的葉子越多. 所以我們將所有的軍隊在不超過當前二分出來的時間的前提下盡量往根節點跳. 如果不能跳了,就「就地駐紮」,控制該節點. 顯然這樣能控制到的葉子是最多的.

這裡我們要明確一下,如果一支軍隊能到達乙個根的兒子並且夠越過根節點,那我們只是把他看成「暫時駐留」在該兒子上,而非「控制了該節點」. 原因後面會加以說明.

第一步:將每支軍隊盡量向樹根移動

然後我們就會碰到乙個問題:有一些葉子是不能被控制到的,而另外一些軍隊卻可以通過根節點跳到其他的子樹上,從而控制其他子樹.

而且顯然只要控制各個當前未被控制的葉子所在子樹的根就行了. 控制這些根,所有的還未被控制的葉子就被控制了.

所以我們現在要做的就是:嘗試將那些能夠越過根節點到達其他子樹的軍隊自身子樹 還存在未被控制的葉子的 根的兒子一一配對,如果所有的根的兒子都能夠與至少一支軍隊配對,那麼當前二分出來的時間顯然成立.

第二步:找到所有子樹中包含未被控制的葉子的根的兒子(這一步可以通過dfs,o(n

)o(n)

o(n)

實現)第三步:嘗試將這些根的兒子和可以越過根的軍隊相對應.

接下來我們要考慮第三步中如何對應的問題.

考慮對於乙個到根所需的時間為tim

etime

time

的根的兒子,如果它要被從其他子樹來的軍隊控制,那麼這個軍隊的剩餘時間需要大於等於tim

etime

time

.這啟示我們可以分別對根到兒子的時間軍隊越過根之後剩餘的時間從大到小排序,然後將兩者掃一遍,如果出現了根到兒子的時間大於軍隊越過根之後剩餘的時間的情況,說明無法匹配,二分出來的時間不成立;否則成立.

為什麼呢?比方說掃到k

kk位置時,k−1

k-1k−

1位置及以前的所有位置都已經匹配完成. 到這個位置時的 軍隊越過根之後剩餘時間 已經是所能提供給這個 根的兒子 的最大值了,如果無法滿足,說明其他所有的匹配方案都無法滿足.

但是!

有沒有考慮過一種情況:某乙個節點上能夠越過根節點的軍隊全部越過了根節點,但是這些軍隊跳走了之後,這個子樹卻不能被完全控制了.

**懸念:這就是之前需要加以說明的原因

那我們怎麼辦?

我們發現,對於乙個根的兒子,有兩種情況:一是它的所有軍隊都跳走了,然後由其他子樹的軍隊來控制它;二是它自身保留一支軍隊控制自身.

所幸我們以上的貪心總體思路仍然正確,排序仍然對需要跨子樹的匹配成立. 也就是說,我們只需要多加考慮這種特殊情況即可.

先講結論:如果掃到乙個根的兒子,而且這個兒子上仍然有暫時駐紮的軍隊,那麼我們就讓這些軍隊中越過根節點後剩餘時間最小的那支軍隊留下;否則沒有暫時駐紮的軍隊的話,就另外找軍隊控制它.

我們考慮這個補丁的正確性. 首先,如果當前這個兒子上有暫時駐紮的軍隊,那麼顯然這些軍隊越過子樹後剩餘的時間都是小於我們從其他子樹調來軍隊剩餘的時間的.(由之前的由大到小排序顯然)那麼我們不如把這個其他子樹調來的軍隊調到下乙個根的兒子,這樣可以使下乙個根的兒子可以成功匹配的可能性更大. 另外一種情況:暫時駐紮的軍隊全部離開的情況,正確性實際上和一開始的貪心一樣,在這裡就不加以贅述了.

貪心總體做法:離根節點遠的兒子由剩餘路程大的軍隊來管轄,但是要優先由本來就在這棵子樹上的軍隊來管轄. 所以我們先檢視在子樹x中,可以到達根節點,且到根節點後剩餘路程最小的軍隊是否被使用(可以開乙個桶記錄這個兒子是否仍有未被使用的軍隊,因為是桶,由於排序後有單調性,就不需要記錄剩餘路程最小的軍隊了);如果被使用,再看當前沒有被使用的軍隊裡剩餘路程最大的可否到達這棵子樹.

倍增:

這東西很簡單,就是在各個軍隊盡量向上跳時減少跳的步數,從而降低時間複雜度.

可能以上我對子樹的說明不夠清楚,語言表達能力還是欠缺啊。。。
#include

#define maxn 50010

#define ll long long

using

namespace std;

struct edgee[maxn<<1]

;int head[maxn]

,tot;

void

add(

int u,

int v,ll w)

int fa[maxn][30

];ll dis[maxn][30

];void

dfs1

(int u,

int pre,ll dist)

}//倍增預處理

ll l,r,ans;

int n,m,army[maxn]

;struct node};

bool ck[maxn]

,vis[maxn]

;int sum[maxn]

;bool

dfs(

int u,

int pre)

ck[u]

=true

;bool flag=

false

;for

(int i=head[u]

;i;i=e[i]

.next )if(

!flag)

else

return ck[u];}

//找到那些沒有被完全控制的葉子所在的子樹

bool

check

(ll x)

if(fa[u][0

]==1&&len+dis[u][0

]sum[u]++;

}else vis[u]

=true;}

if(dfs(1,

1))return

true

;for

(int i=head[1]

;i;i=e[i]

.next ));

}//找到沒有被封死的子樹

if(g.

empty()

||f.

empty()

)return

false

;sort

(f.rbegin()

,f.rend()

);sort

(g.rbegin()

,g.rend()

);//由大到小排序

int k=0;

for(

int i=

0;isize()

;i++

)else

//跳過那些軍隊都被用完的根的兒子

sum[g[k]

.pos ]--;

if(g[k]

.len.len)

return

false

; k++;if

(k>=g.

size()

&&isize()

-1)return

false;}

}return

true;}

intmain()

dfs1(1

,1,0

);for(

int i=

1;i<=

20;i++

)//dis表示到第2^i輩祖先所需的時間

}scanf

("%d"

,&m)

;for

(register

int i=

1;i<=m;i++

)scanf

("%d"

,&army[i]);

//哪些節點上有軍隊

while

(l<=r)

//二分答案

printf

("%lld\n"

,ans)

;return0;

}

特別鳴謝:思路提供 @litble

P1084 疫情控制

p1084 疫情控制 好像二分 倍增 樹上差分是比較熱門的考尻點 會結合在一起考,難度比較大,需要多加練習。現在在解決noip最後的幾道大題,很鵝心。也沒有人做嚮導,很難受qwq 首先這是一棵樹,乙個軍隊肯定是越往上走越好。有大佬說過,對於這種提點的題,要是用倍增 要是時間最短,就是要是用時最長的軍...

P1084 疫情控制

p1084 傳送門 感覺noip t3也是有點東西的 將該題轉化為最大值最小問題後想到二分答案 接下來考慮 check 時如何貪心 由於除了在根節點所有軍隊都只往上跳明顯採取倍增的方式 記錄所有能到達根節點的軍隊和根節點下所有未被封死的子樹 將兩個序列從小到大排序後貪心匹配即可判斷 注意 在判斷封死...

P1084 疫情控制

p1084 疫情控制 hh 國有 nn個城市,這 n 個城市用n 1條雙向道路相互連通構成一棵樹,1號城市是首都,也是樹中的根節點。hh國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市 葉子節點所表示的城市 決定動用軍隊在一些城市建立檢查點,使得從首都到邊境城市的每一條...