比賽連結: http://codeforces.com/contest/351
Problem A - Jeff and Rounding
給你\(2N\)個數字(不一定是整數),你要做N次操作,每次操作的內容是挑兩個還沒被選過的數字,一個取上高斯,另一個取下高斯,要最小化(一開始的總合)-(操作之後的總和)。
題解:
對於那些整數們,顯然不管發生什麼事他們的大小都不會改變,然後對於那些非整數,會發現你無力改變整數部分,只能把那些零點多的數字改成\(0\)或\(1\),\(1\)的數量的上下界可以用所有非整數的數量跟\(N\)算出來,所以我們可以枚舉我們要把幾個零點多改成\(1\),剩下就變成\(0\),細節就看code吧,因為不喜歡double所以我把輸入轉成整數做。
code: http://codeforces.com/contest/351/submission/16270303
Problem B - Jeff and Furik
給你一個數字都相異的序列,Jeff跟Furik會對著這個序列輪流做操作,Jeff先手,每次Jeff可以隨便挑兩個相鄰的數字然後交換那兩個數字,而每次Furik會擲一個公正硬幣,如果是正面就交換一個相鄰的且左邊比較大的兩個數字,反面的話就交換兩個相鄰且右邊比較大的數字,如果不符合他的要求的話他就會在擲一次硬幣,問你在Jeff盡全力的情況下,序列變回遞增的期望交換次數。
題解:
要先知道逆序數對數就是最少必須被交換的次數之類的,而Jeff毫無理由不讓逆序數對數-1,Furik就隨便他玩,設\(dp_i\)=(逆序數對為\(i\)時的期望次數),列一下式子就會得到\(dp_i=dp_{i-2}+4\)之類的,又\(dp_1=1, dp_2=4\),同時可以得到答案會是逆序數對數乘上二,然後如果逆序數對數是奇數就再減一之類的亂七八糟公式。
code: http://codeforces.com/contest/351/submission/16270651
Problem C - Jeff and Brackets
給你\(N, M\),\(M\)保證是偶數,要做出一個長度為\(NM\)的括弧序列,index是\(0\)~\(NM-1\),其中第\(i\)個位子當成上括弧的成本是\(a_{imodN}\),當下括弧的成本是\(b_{imodN}\),問你做出整個括弧序列的最小成本是多少。
題解:
先把\(N\)改成\(2N\),\(M\)改成\(M/2\)(以下都用新的\(N, M\)講),然後做出長度為\(N\)的合法括弧序列的最小成本,DP或greedy之類的,然後我們就得到了一個最小成本跟一個方案,然後用倍增做。先解釋一下為什麼可以倍增,假設有兩個合法且成本最小的括弧序列接在一起,要把他換成最小成本的方法一定是找到一對在不同邊的的")(",然後把他換成"()",一定不可能需要改變一對"()"的原因是,把"()"換成")("的話一定會讓中間的某個地方爛掉,如果真的要這樣換也一定需要另外一對")("換成"()"讓他變合法。
........)...(...)...(.....
........a...b..c..d.....
a, b, c, d分別表示翻轉對應括弧後,成本變小的量(可以是負的),如果有某個b+c很大,他也一定要一對a+d來支援,那麼就算我們只找")(",也可以變成換a+b跟c+d。
講得有點混亂,直接講做法的話就是,先做出一個N的括弧序列的答案之後,做出2N, 4N, 8N, 16N.....的答案跟方案,雖然序列長度會變得很大,但是我們其實只有2N種成本,所以只要用2N個數字就可以描述一個方案(有幾個在第i個位子的下括弧之類的),然後一直找一對最好的")(",把它換成"()",因為上下括弧獨立所以可以找個別的最好的,因為最多就是換成(((())))之類的,所以更新一定會停止,反正N小小的所以可以亂做一通。最後再用這些二的次方湊出M就好了,用一樣的方法合併兩個序列。
官方tutorial的做法是做出一個轉移矩陣,感覺很神秘。
code: http://codeforces.com/contest/351/submission/16290079
Problem D - Jeff and Removing Periods
Jeff可以對一個序列{a}做以下操作:
1. 找一組(v, t, k),滿足\(a_v=a_v+k=a_v+2k+...=a_v+tk\),反白(?)這些數字。
2. 把剛剛反白的數字砍掉,剩下的數字重新由1~n編號。
3. 把剩下的數字照你喜歡的樣子排列。
上面的步驟為一次操作。
一個序列的美麗度的定義是要做幾次操作可以把數列裡的元素全部砍掉。
給你一個長度為N的序列跟M次詢問,每次詢問是一個區間的美麗度。
題解:
首先會發現,可以自由排列剩下的數字是個非常OP的行為,只要可以自由排列,就可以把1, 2, 3, 1, 3, 2, 1, 1, 1排列成1, 1, 1, 1, 1, 2, 2, 3, 3之類的,很顯然的排列之後每個數字就只要一次操作就可以被砍完,這也顯然是答案的下界。思考一陣就會發現美麗度是(相異的數字種類) + x,如果有一個數字一開始可以被一次操作拿完的話x就是0,反之x就是1。區間相異數字是個頗經典的題目,可以把詢問離線照右界排序,數字由左而右一個一個加入,然後數的時候只數每個數字最後一次出現之類的,可以用線段樹或BIT做。至於後面那個問題,其實可以維護每個數字在當前右界中,往左最多可以到哪裡仍然可以被一次拿完,比如說:
2_____2__2__2__2____________|____________2
a c b
當右界在b的位子時,左界只要在a右邊,2就可以一口氣被拿完,而2這個數字會在下一個2出現之後改變他的左界情況。如果我們定義\(hd_i\)是第i個數字以一樣的間隔不斷地往前一個同數字跳,跳到哪一個位子會爆炸(上示意圖中,\(hd_c = a\))。那麼對一個l, r詢問,有一個數字能一口氣被拿完<==>存在l<=k<=r使得\(hd_k\)<l,這件事可以用線段樹做,不過要記得加入新的數字之後要把那個數字的前一次出現的hd改掉,比如說新出現了一個2,要把上一個2的hd改掉。然後我們這題就可以做了。
官方tutorial只講說可以用根號作法做,寫lgN覺得開心xDD。
code: http://codeforces.com/contest/351/submission/16346528
Problem E - Jeff and Permutation
給你一個序列,你可以把一些數字改成負的,讓逆序數對數盡量小。
題解:
會發現對於最大的數字,其他數字的正負不影響它,如果有多個一樣大的數字,你一定只會想要讓前面幾個數字是負的,然後後面都是正的,N又小小的,所以就有了一個很開心的做法,細節就直接看code吧,用文字不好解釋。
官方tutorial的做法是個奇怪的DP,有點神秘。
code: http://codeforces.com/contest/351/submission/16296103