[TJOI2013]拯救小矮人(反悔贪心证明),「ICPC World Finals 2019」Hobson 的火车(基环树,差分)
2021-09-07 test
- [TJOI2013]拯救小矮人
- 「ICPC World Finals 2019」Hobson 的火車
[TJOI2013]拯救小矮人
luogu4823
考試題目的數據加強為2e5,所以此題做法應為O(nlog?n)O(n\log n)O(nlogn)的反悔貪心
這種有多元屬性,選擇最優的問題
如果發現簡單的貪心總是半錯半對的
解法通常有兩種
- 設計dp進行轉移
- 考慮反悔貪心
此題也不意外
定義每個人所需的逃出高度為H?ai?biH-a_i-b_iH?ai??bi?
observation1 : 逃出高度越高的人越先逃出
當然可能某些人的屬性是極端的,比如身高非常高而手很短,卻被派到前面,這個時候可能不讓此人逃走能讓后面的人走得更多,才是最佳方案
從這種情況上得出
observation2 : 如果斷定此人不逃出,那么就永遠不會再給機會逃出
observation3 : 如果此人不能逃出,但是前面逃出的人有高度更高的,就把最高的那個人扔回來,換成這個人逃出
到這里就可以使用優先隊列維護做了
接下來給出性質一和性質三的證明
-
性質一:
考慮先逃走的人逃出高度為h1h_1h1?,再逃走的逃出高度為h2h_2h2?
則有h1>h2h_1>h_2h1?>h2?,即H?a1?b1>H?a2?b2?a1+b1<a2+b2H-a_1-b_1>H-a_2-b_2\Rightarrow a_1+b_1<a_2+b_2H?a1??b1?>H?a2??b2??a1?+b1?<a2?+b2?
設hhh為還在洞內所有人的身高包含1,2
如果1,2都能逃出,那么1,2之間逃出的順序,對后面的人不會影響,反正都不會有身高的貢獻
也就是說,我們需要證明當滿足h1>h2h_1>h_2h1?>h2?條件時,1更難逃出,所以需要先走
既然2能逃走,說明不需要1的身高,即h?a1+a2+b2≥Hh-a_1+a_2+b_2\ge Hh?a1?+a2?+b2?≥H
如果2先逃走,1也能逃走,則必須滿足h?a2+a1+b1≥Hh-a_2+a_1+b_1\ge Hh?a2?+a1?+b1?≥H
-
?a1+a2+b2??a2+a1+b1?a2+a2+b2?a1+a1+b1-a_1+a_2+b_2\quad?\quad -a_2+a_1+b_1\Leftrightarrow a_2+a_2+b_2\quad ?\quad a_1+a_1+b_1?a1?+a2?+b2???a2?+a1?+b1??a2?+a2?+b2??a1?+a1?+b1?
a1+b1<a2+b2?a1<a2+b2?b1a_1+b_1<a_2+b_2\Rightarrow a_1<a_2+b_2-b_1a1?+b1?<a2?+b2??a1?<a2?+b2??b1?
a1+a1+b1<a2+b2?b1+a1+b1=a2+b2+a1a_1+a_1+b_1<a_2+b_2-b_1+a_1+b_1=a_2+b_2+a_1a1?+a1?+b1?<a2?+b2??b1?+a1?+b1?=a2?+b2?+a1?
所以?應為>
也就是說2逃出時超出HHH的距離更多,也就是比1逃出條件要松一點
如果1逃走后2無法出逃,那么2逃走后1更無法逃出,且加緊了后面的約束
當然這只是充分證明,因為會考慮逃不走換人的情況,結合反悔貪心才是最后的貪心
-
-
性質三:
對于某個不能逃出的人,選擇前面比自己高的最高的人,換進來
這反悔貪心很顯然,換一個人進來換一個人出去,對人數沒有影響
但是換了個更高的人進來,那么其高度就會松弛后面所有人的逃出條件,使得逃出可能性增大
-
接下來證明,在逃出條件越后越松弛的前提下,換一個更高的人進來,自己一定可以逃出去
假設2此時無法逃出,需要把前面的1換進來,定義hhh為在洞內的人的高度加上已經逃出的1的高度
則a1+b1<a2+b2a_1+b_1<a_2+b_2a1?+b1?<a2?+b2? 且 a1>a2a_1>a_2a1?>a2?
把在1,2之間逃出洞的所有人先再次丟回洞中,相當于回溯到第一次決定讓1逃出的時刻
因為逃出高度限制是逐漸寬松的,既然這個時候1能逃出,2一定也能逃出
考慮變成2逃出對之前在1,2中間逃出的人的影響,發現是正面影響
因為2的高度沒有1高,相當于換了一個更高的人墊背,那么原來在1,2中間逃出的人同樣也會逃離
-
「ICPC World Finals 2019」Hobson 的火車
LOJ6548
恰好考了一下最近學的基環樹
如果是問從每個點開始能訪問的點數,那么就非常簡單,是個外環樹
每個非環樹上的點的路徑都是一條鏈,只需要考慮深度和kkk的關系即可
但不巧的是這道題是求每個點可以被多少個點訪問到,是個內環樹
發現時間卡在設計DPDPDP的kkk轉移上
很妙的,又是前幾天考試的解法就是——樹上差分和環上差分
具體細節實現可看代碼及注釋
#include <cstdio> #include <vector> using namespace std; #define maxn 500005 vector < int > G[maxn], circle; int n, k, siz, top; int ans[maxn], dep[maxn], vis[maxn], s[maxn], d[maxn];void dfs( int u, int rt ) {if( ! vis[u] ) vis[u] = 1; //在求該聯通分量時可能有些點還未訪問過 身處環遍歷后面 s[++ top] = u;if( top > k + 1 and s[top - k - 1] ^ circle[rt] ) //差分 與u點相距k+1甚至更遠的點不再有貢獻 ans[s[top - k - 1]] --;for( auto v : G[u] )if( vis[v] == 2 ) continue;//環點在后面單獨更新else { dep[v] = dep[u] + 1;dfs( v, rt );if( u ^ circle[rt] ) ans[u] += ans[v];}top --;if( dep[u] <= k ) { //會延伸到部分環點 哪怕只有一個點 //環長雖然是siz 但實際上只需要走siz-1步就會遍歷完所有環點 if( dep[u] + siz > k + 1 ) { //無法在k步內將環都覆蓋完 int t = ( rt + k - dep[u] + 1 ) % siz;/*rt+k-dep[u]是真正差分的結束位置需要在結束位置的下一位pos+1打上-1抵消標記 */ans[circle[rt]] ++, ans[circle[t]] --;if( t < rt ) ans[circle[0]] ++;/*由于環差分也是從環頭遍歷到環尾 默認斷開了環頭與環尾的邊 該if成立說明覆蓋的部分點跨越了環尾在環頭多延伸了一點 環頭到結束位置也應該差分*/ }else ++ ans[circle[0]]; //該點可以訪問所有環點 直接在環頭++ }if( u ^ circle[rt] ) ans[u] ++; }void dfs( int now ) {circle.clear();while( vis[now] ^ 2 ) { //如果now點先前已經被遍歷2次 也就是回到了環頭 不再重復入環 if( vis[now] ) circle.push_back( now ); //再次經過now點說明是環上一點 開始進入求環部分 vis[now] ++;now = d[now];}siz = circle.size();for( int i = 0;i < siz;i ++ )//先差分求每個環點的非環樹答案 dfs( circle[i], i );for( int i = 1;i < siz;i ++ )//環差分 ans[circle[i]] += ans[circle[i - 1]]; }int main() {scanf( "%d %d", &n, &k );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &d[i] );G[d[i]].push_back( i );//建反圖 }for( int i = 1;i <= n;i ++ )if( ! vis[i] ) dfs( i );for( int i = 1;i <= n;i ++ )printf( "%d\n", ans[i] );return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的[TJOI2013]拯救小矮人(反悔贪心证明),「ICPC World Finals 2019」Hobson 的火车(基环树,差分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习入门(一):LeNet-5教程与
- 下一篇: 安卓截屏长图怎么弄得(安卓截屏长)