CF Round 700 Div2 解題補題報告

2022-04-30 19:00:10 字數 4840 閱讀 6113

官方題解

這把被吊錘就離譜,尤其是b題寫的都失了智了

給定乙個字串 \(s\),兩個人 \(a\) 和 \(b\) 玩乙個小遊戲,每個人輪流出手,將某個位置的字元變換一下(不能和原來相同),每個位置僅能更改一次。\(a\) 的目標是使得最後的字串的字典序盡量大, \(b\) 則相反。如果兩人均採取最佳策略,問最後 \(s\) 會變成啥。

\(|s|\leq 50\)

看完題目,看下樣例,答案就很顯然了:

兩個人都會盡可能選靠前的字元進行修改

對於 \(a\),為了使字串字典序盡量大,肯定是能改成 \(a\) 就改,改不了(原來就是 \(a\))就改成 \(b\)

對於 \(b\),思路也類似

沒啥好說的,具體看**

#include using namespace std;

char s[100];

void solve()

int main()

有個英雄,每次攻擊傷害為 \(a\),自身血量為 \(b\)。現在有 \(n\) 個怪物,第 \(i\) 個怪物傷害為 \(a_i\),血量為 \(b_i\)。

現在英雄每回合選乙個怪物與之對戰,一回合下來,兩個人各自減少對應的血。如果誰血量小於等於 \(0\),那麼他就死了。

現在想要判斷,能不能乾掉全部怪獸(和最後乙個怪獸同歸於盡也行)

顯然必須消滅全部怪獸,那就乙個乙個打就是了,看看能不能全部打掉

這樣寫了一發程式,然後榮耀的 \(wa\) 了

這時候群裡面有大(han)佬(pi) 說,要排序,要排序!

我一聽,確實,是這樣的,打怪的順序對於我們確實有影響,例如下面這組資料:

10 100 2

110 30

10 20

很顯然,如果我們先和第乙個怪物打,那就會和他同歸於盡,所以應該先和第二個打,這樣才能將怪獸全部擊敗

(一種貪心策略:對於某些怪獸,如果注定和他同歸於盡或者大批掉血啥的,我們盡量往後拖,然後先打戰鬥力低的)

對於第 \(i\) 個怪獸,我們可以將 \(b_i\) 轉變為它可以挨英雄打的次數,也就是 \(b_i=(b_i + a - 1) / a\),然後這個怪物戰鬥力就是 \(a_ib_i\),按照這玩意排序,然後順著打就完了。

但我們有找到了一組反例:

10 102 3

1 100 20

10 10 50

這資料已經排好序了,但是顯然打第三個怪物會 \(gg\),但是我們應該先打第三個,然後和第二個同歸於盡才是最佳策略。

懂了,\(a_ib_i\) 相同時候,把 \(b_i\) 小的排在後面

不好意思,又 \(wa\) 了,我們可以找到這樣一組反例(對上面那個樣例修改一下):

10 102 3

1 99 20

10 10 50

我就問,你現在還能怎麼排?

我相信有不少大佬還能排,而且比賽的時候能過(隨後被 \(hack\) 的體無完膚),但是我們現在應該跳出這個思維了:按照特定思路貪心並且排序,真的是正解嗎?

其實經過上面的一系列操作加上 \(wa\) 之後,我們基本上已經清楚了消滅所有怪獸的一種基本策略:

先消滅 \(n-1\) 個怪獸,並且保證消滅完之後英雄還活著

消滅最後乙個怪獸,要麼全身而退(打完還活著),要麼同歸於盡

對於全身而退的方式,我們上面所有的方案都沒問題,重點在於這個同歸於盡:在血量不夠的情況下,盡量最優化,和最後乙個怪物換掉

這東西是沒法貪心選出來的,但是我們發現,這東西似乎可以線性列舉(就離譜):列舉和哪乙個怪獸最後打,然後看看別的 \(n-1\) 個怪獸能不能全部消滅,然後和最後乙個怪獸嘗試互換一波

越說越迷糊了,直接上**了(昨晚把我折磨傻了):

#include using namespace std;

#define ll long long

const int n = 100010;

int n;

ll a, b, a[n], b[n];

bool solve()

for (int i = 1; i <= n; ++i)

return false;

}int main()

這是乙個互動題。

給定乙個 \(1\) 到 \(n(1\leq n \leq 10^5)\) 的排列 \(\\),但是我們只知道 \(n\) 的大小,不知道排列的具體情況。

我們每次可以向評測姬詢問,問 \(a_i\) 是多少 \((1\leq i \leq n)\),詢問次數不得超過 \(100\) 次。

現在我們希望找到乙個正整數 \(k\),使得 \(a_k且 \(a_k(我們預設 \(a_0=a_=+\infty\))

好,這題重新讓我認識了二分(就離譜)

\(100\) 次找 \(10^5\) 規模的資料,不用懷疑,二分沒跑了,主要是:這 b 玩意咋二分?

我們不妨設答案區間為 \([l,r]\),也就是該區間必然存在 \(k\),使得 \(a_>a_k

我們令 \(mid=(l+r)/2\),查詢 \(a_\) 和 \(a_\) 的值。

證明:當 \(a_ < a_\) 時,區間 \([l,mid]\) 必然有解:

我們可以反證,假設該區間無解:因為\(a_>a_l\) 作為先決條件必然成立(如果不懂就先接著看,待會就懂了),所以為了保證無解,\(\\) 在區間 \([l,mid]\) 上必須單調遞減,否則必然能夠找出反例。尷尬的是,此時恰好出現 \(a_>a_\) 的情況,說明 \(mid\) 是乙個可行解。

反證矛盾,證明正向結論成立。

反之,如果 \(a_>a_\) 時,區間 \([l+1,r]\) 必然有解,證明同上。

如果嚴格遵守這套遞迴流程,那麼可以保證邊界條件成立。

好了,一套數學證明下來,我們便找出了二分策略,很顯然,詢問次數不會超過 \(100\) 次。

這題難度我給一分,因為我乙份抵別人一伯分(逃

#include using namespace std;

const int n = 100010;

int n, a[n];

void query(int x)

int main()

printf("! %d\n", l);

fflush(stdout);

return 0;

}

給定乙個數列 \(\\),嘗試將其分成兩個數列 \(\\) 和 \(\\) (這兩個數列允許是空的)。

現在給定乙個操作 \(seg\):對於乙個數列,在不排序的情況下將其去重(也就是只去重那些相鄰的),然後剩下來的那個數列的長度,稱其為該數列的 \(seg\) 值。

現在嘗試找出一種分法,使得 \(seg(b)+seg(c)\) 最大,並且輸出該最大值。

\(1\leq n \leq 10^5,1\leq a_i\leq n\)

這題目似乎只能貪心來寫就離譜。

我們不妨記 \(\\) 的最後乙個元素是 \(x\),\(\\) 的最後乙個元素是 \(y\) ,\(o(n)\) 線性掃瞄陣列 \(\\),記掃瞄到的數字是 \(z\) 。

顯然有這樣的貪心策略(雖然這玩意證明就離譜):

當 \(x=z\) 時就把 \(z\) 插到 \(\\) 後面,反之亦然;如果\(x=y=z\),那就隨便插

這個應該比較顯然了吧,這種插法但凡修改一下,都不會使得答案更優

當 \(x\not=z,y\not= z\) 時,這時候理論上插在誰後面都行,但是這裡有個小問題:

打個比方,例如數列 \(\\),此時 \(\ = \) ,\(\=3\),這時掃到 \(z=5\),顯然,我們應該把這個數插到 \(\\) 而非 \(\\) 後面。

這裡引入乙個 \(next\) 函式,例如 \(next(x)\) 表示 \(\\) 下乙個值為 \(x\) 的下標。例如上述情況下, \(next(y)=4\)

有個似乎比較顯然的結論:當 \(x\not=z,y\not=z\) 時,雖然兩種插法都行,但是最好當 \(next(x) < next(y)\) 時插在 \(\\) 後面, \(next(y) < next(x)\) 時插在 \(\\) 後面(趕緊堵住,防止到時候重複)。

這玩意只能說是靠直覺感受出來的,但是證明並不顯然。實際上,我看了官網的題解證明,還以為點錯了題,點進了 div2 d。所以說這玩意感受下就好,不要強求證明了(反正我肯定不會

現在考慮下如何維護這個 \(next\) 了,畢竟暴力的複雜度是 \(o(n^2)\),鐵超時。我們可以開乙個 \(pos\) 陣列維護一下,這樣就可以 \(o(n)\) 從後向前掃一遍就出結果了(詳情見**)。

這裡有個小細節:為了方便,我是一開始預設將前兩個數給分別分到 \(\\) 和 \(\\) 裡面的,但是在第 8 個點 \(wa\) 了,(因為前兩個數在部分情況下應該放在乙個陣列裡面,雖然我還沒有找到乙個足夠簡單的例子),所以我又稍微改了下,變成下面這段**(不僅思維難度高,而且**本身還有億些小細節):

#include using namespace std;

const int n = 100010;

int n, a[n], next[n], pos[n];

int main()

//output

printf("%d", cnt1 + cnt2);

return 0;

}

這題和上一題的區別在於,這裡是嘗試使得表示式的值最小。

不會,告辭。

CF Round605比賽總結

a three friends 題意 給出三個點的座標,每乙個點可以進行左移或者右移單位1,或者不移動,問最後三個點之間的相對距離最小值是多少。解題思路 因為每乙個點最多三種情況,所以列舉一下即可。如下 include using namespace std typedef long long ll...

CF round 292 解題報告

a題 給出乙個座標 a,b 和走的步數,問有沒有可能從 0,0 走這麼多步剛好到達 a,b 水題,結果還是wa了一次,沒有考慮到a 0或者b 0的情況。1 include2 intmain 319 20return0 21 view code b題 主人公有n個男性朋友,為0到n 1,m個女性朋友,...

面試70技巧

1 請你自我介紹一下你自己,1 如果你確實熱愛這個職業,那你就要不斷學習,虛心向領導和同事學習業務知識和處事經驗,了解這個職業的精神內涵和職業要求,力爭減少差距 2 你覺得這個職業可有可無,那還是趁早換個職業,去發現適合你的,你熱愛的職業,那樣你的發展前途也會大點,對單位和個人都有好處。10 在完成...