2015年3月25日 星期三

SRM654ˋ

向來在台灣時間早上的場次或許因為參加的人偏少,題目有偏間單的趨勢,這場也不例外,我難得沒有重大失誤的破台了。不過我每一題都 debug 好久就是了。

這場第一名Endagorion寫的 Editorial

250 --- SquareScores


題意:給一個由小寫字母和問號構成的字串,並且給你問號代表每個字母的機率,並定意一個字串的分數為所有字母都相同的 substring 個數 (不同位置算不同)。求這個字串的分數期望值。

數據範圍:字串長度($n$)不超過 $1000$。

tag:[ 數學 ] [ 期望值 ]

關鍵字:簡單期望值

= = = = = = = = = = = = = = =
就直接運用期望值可以拆開的概念,全部也只有 $n*(n-1)/2$個子字串,若再多考慮每個字串相同的字母是哪個的至多 $26$ 種可能性去枚舉,最裸的做法時間複雜度是 $O(26n^3)$,但是$s[i...j]$ 全為同一個字母的機率可以由 $s[i...j-1]$ O(1) 轉移得知,所以立刻就壓到 $O(26n^2)$ 了。但實際上這題根本可以做到 $O(n)$,大家可以想想看~。這題算是期望值類的老掉牙的題目吧。

450 --- SuccessiveSubtraction2

題意: 給定一個數列 $a_1 \sim a_n$,考慮算式 $a_1 - a_2 - a_3 - ... - a_n$,我們可以至多家上兩組括號,求最大的可能計算結果是多少。

數據範圍:$1 \leq n \leq 2000$,有至多 $2000$個詢問(每個詢問是把上一筆詢問的其中一個數字改成另外一個數字)。

tag:[ dp ]

關鍵字:最大連續和

= = = = = = = = = = = = = = =
自己亂補上刮號就可以發現,補至多兩個括號,等價於把 $a_3 \sim a_n$ 中至多連續的兩段數字正負對調。

這題做法一堆吧,限制非長寬鬆,加上他每筆詢問實際上只有變動一個數字,那應該是可以做到 $O((q+n) log n)$ 的。

我的做法是 dp 做出每個 prefix 和 suffix 的最大連續和,然後枚舉prefix 和 suffix 切開的位置。

這題還有一個有趣的做法(參考此連結的),先找一次最大連續和的一段正負反過來,然後再做一次相同的事就行了。

850 --- TwoEntrances

題意:給一個長的像 Tree 的家,由兩個 node 是家的入口,現在要搬家具進去每個 node,但是能般進去的條件是,由某個入口到該 node 的 path 上都還沒有放入家具,問般家具的順序有幾種?答案 mod $10^9+7$。

數據範圍:邊數不超過 $3000$。

tag :[ dp ] [ 排列組合 ]

關鍵字:燈飾 dp

= = = = = = = = = = = = = = =

私認為這題難度只有 $500$,若入口只有一個,那他就只是一個非常經典的題目,我這個  BLOG 已經出現過了... 請看 CF290 pD

若是這樣的問題相信不少人都見過,但入口兩個要怎麼處理呢?

先畫意張示意圗 :)

黑點是入口,我們想像一下把代表入口的兩個點釘在牆上掛起來,大概就會變成上圖所示。

我們先想想退化的 case,例如說...掛起來後沒有任何點垂下來。我們會發現,這種 case 的話,任何時刻有擺家具的 node 都是連在一起的!於是就給了我一個靈感,我們可以 dp 狀太設成: $dp[l][r]$ 代表 $p_l \sim p_r$間及其垂下來的所有 node 都擺上家具,但其他 node 都沒有的方法數。

至於要怎麼轉移呢?我們可發現,代表 $dp[l][r]$ 的狀態,最後一個擺上家具的位置一定是 $p_l$ 或 $p_r$ 兩者之一,於是我們分別把 $p_l$ 和 $p_r$ 當作 root,然後想像你是怎麼解原始的經典題版本,那應該就會知道要怎麼轉移了 :)


2015年3月23日 星期一

VK Cup 2015 - Round 1

※ 本篇不含題解,只有簡單記錄一下比賽心得,方便以後搜尋及回憶

= = = = = = = = = = = = = =

馬上就毀了不久前說的 V 字形目標 XD

這場比賽無疑是我寫解題報告至今最簡單的一場Div. 1比賽,可是我出太多包了就掛掉了 Orz 總覺得當我有明確目標的時候就一定會爆炸 ... (看看我至今為止進不了任何 online round)。

這場比賽有幾個特殊的地方:1. 難度沒有按照順序排 2. Codeforces 第一次採用以 250 分為間隔的動態分數。

Problem B --- Group Photo 2 (online version)

tag:[ 枚舉 ][ greedy ]
關鍵字:矩形橫擺直擺

賽前我並不知道題目沒有按照難度排,但尤於求攻心切就先開了 pB (自負能以差不多的時間寫完 A 和 B),但是大概是太急躁了 ... 過了三分多鐘我都還沒看懂題目,在想要不要先回來讀 A 的題目時先看了一下 scoreboard,看了感到非常莫名, pA沒有人上傳,倒是 pE 有少數幾隻貓上傳 ... 這時心裡猜想或許 pE 是 well know 的難題才會發生這種事?於是決定繼續試圖理解 pB,終於理解意思後,立刻把這題連想到我曾經在 Topcoder SRM629 出的 Div1 250 都是橫擺或直擺的矩形問一些事情,馬上就會做了。

Problem E --- The Art of Dealing with ATM

tag: [搜索]
關鍵字:零錢

pB 過了 pretest 後看了 scoreboard,發現有大量的人傳了 pE,以及少數人傳 pB,但沒有人傳 p ,於是我開始相信這場難度沒有按照順序排,但看完 pE想了一下後並不知道怎麼做 ...,賽後看了別人 AC 的 code 才發現我看錯題目了。我沒有看到題目說的每次提領的錢至多只能提領兩種幣額的限制,賽中我不時回來思考這題,花了不少時間。

Problem D --- Social Network

tag:[貪心]
關鍵字:區間貪心

雖然 pE 卡住了,但是一直卡下去很不妙,所以就繼續開其他題,這題題目對我來說也是很難讀懂,但也是讀懂後就會做了,而且 code 很簡短,一個 for 迴圈掃過去就結束了。在我感覺上這題只有一般 Div. 1 B 以下的程度。

Problem A --- And Yet Another Bracket Sequence

tag:[字串比對][ 資料結構 ]
關鍵字:合法括弧

這題我也是看完題目後就會做了,但是覺得可能還會有比這題好寫的題目,在寫這題前先去看了 pC。

Problem C --- Rooks and Rectangules

tag:[ offline ] [ 資料結構 ]
關鍵字:矩形區域類資料結構

這題我倒是沒有讀完題就知道怎麼做 ...,應該說我第一個想到的做法有點困難,但看別人 AC這題的速度大概可以知道這題有更簡單的方法,但我覺得比起去思考更簡單的方法,還是去抄抄 codebook 寫 pA 會比較容易。賽後多想個幾分鐘就想到簡單的做法了。

我複製了 suffix array 和線段樹求 RMQ 的 codebook 迅速的寫完 pA, 但是之後一直 WA 在 pretest 第 12 組,直到賽末。賽中一直 de 不出 bug 時超心急的,我已經好久沒有卡 bug 卡那麼久了,我覺得 code 完全有按照我的思考寫啊!當下覺得要不是我想到的做法是錯的,要不然就是我的 codebook 有錯,可是無論重新檢視自己的做法證確性多少次,都還是覺得是對的,並且我那兩份 codebook 都用了好多次了。又重新看了幾次這份 code 我沒抄 codebook 的部分,都找不到錯誤,過程中索性先回去繼續想 pE 轉換情緒幾次,但也都處處碰壁。直到最後,沒有奇蹟。這場比賽我以兩題作收。

事後沉澱下繼續找 bug,當時心想,要是真的是 codebook 有 bug 的話那還蠻糟糕的,所以也幫抄 codebook 的部分 debug,然後就發現我的 bug 了...,原來我的 RMQ codebook 只當所有數是非負才會對 ...,把這部份修過後重上傳,在第 14 組 test TLE 了...,這也太崩潰 Orz 這代表就算我 RMQ 的 codebook 沒有 bug,我也一定無法 AC 這題 ...。難道我的方法太慢了嗎?我看了一下別人的 AC code,複雜度也和我一樣呀!也是一個 suffix array加一個 O(n log n) 的資料結構,我在測了一下我的 code,發現在 suffix array 的部分就 TLE 了 ...,這是怎樣,這已經是過去我把我自己寫的 suffix array code換成對友寫的快我幾倍的 code耶,結果還是不夠快 Orz,最後我去複製了一分別人 AC 的 code 裡的 suffix array 的部分蓋掉我用的 suffix array,終於 AC了。

雖然 rating 大降,不過這場收穫算是很多吧,把我的suffix array codebook 變得更快,以及除掉 codebook 裡的其中一個潛在 bug。不過上一篇 BLOG 裡寫的目標失敗了還是令人非常傷心 OAQ

2015年3月19日 星期四

Codeforces Round #296 (Div. 1)

這場比賽題目也偏簡單 (個人覺得),至少都算是常見的題型(個人覺得),但是仍然沒有人破台。據個人觀察,近一年內很多上古英武有名的參賽者如 tourist 的參賽頻率都變少了,所以這場我才有機會拿到第三名 >_<,要不然你們想想看,如果 tourist、Petr、peter50216、rng_58、WJMZBMR、ACRush之類的高強度且高穩定性的人傾巢而出的話,我不知道要排到第幾名去了 Orz。

在打解題報告之前先附上一張我 dreamoon 這個 id rating 變紅後的走勢圗!


我現在正在企圖描繪一個嚴格遞降再嚴格遞升的V字型,而且希望右端點能比左端點高 XD 究竟下一場能不能做到呢 >_<,可是下一場是 Div.1 only 的 VK Cup - Round 1 的 online mirror,依經驗這樣的比賽參賽者會少,而且厲害的人會變多,這樣很可能會不僅沒有升高,反而使我的 rating 就此跌下去 ... 好想戰略性撤退 Orz,啊啊啊有這種想法的我好沒有骨氣喔 OAQ

順帶一題近年來的 rating 和台灣的物價一樣,上漲很嚴重,所以畫 V 字才沒那麼難,畫 V 字左端時,深紅色以上的人才 18 個人左右,全世界成為深紅色的人不過三十個,但現在當過深紅色的人已經滿街都是了 ...

這場比賽雖然簡單,但是很多題都有不只一種解法值得我一題,要不然原本比完時覺得題目太簡單不想寫這篇的說 XD

要看官方的 editorial 請點我

以下採用倒敘法從最後一題開始

Problem E --- Triangles 3000

題意:給平面上 $N$ 條直線,請問任取三條直線的所圍成的三角形面積期望值。

數據範圍:$e \leq N \leq 3000$,任兩條線不平行,且任兩條直線的交點座標絕對值在 $10^6$ 內。

tag:[ 計算幾何 ]

= = = = = = = = = = = = = = =
由於題目敘述超短,我寫完前三題後是先看這題的,看來這題時限後,我想了一下下馬上想到一個 $O(N^2 log N)$ 的做法,可是覺得並不好寫所以就回去看 pD 了,pD 上傳並放棄掙扎後,只剩下二十出頭分鐘,覺得時間不夠用,所以就去 challenge 別人。

但是賽後發現,我的思考完全被題目時限限制住了 OAQ,因為我知道若這題官方有比 $O(N^2 log N)$ 更快的做法,就一定不會讓時限那麼長,就以為大概不會有複雜度更好甚至更好寫的做法...,而最後可以發現,官方給出的解答也只是 $O(N^2 log N)$,但若去大家上傳的 code,就可以發現至少存在兩個 $O(N^2)$ 十分鐘內可寫完的做法,我的 $O(N^2 log N)$ 方法也和官方的不一樣,所以這題或許千百人來想就會有千百種做法。

$O(N^2 log N)$ 的做法我就不題了,我來介紹一下官方解答理沒提到的兩種 $O(N^2)$ 做法。

要求期望值,其時就是要你求所有 $C_3^N$ 種取直線方法的三角形面積總合。故要給出一個 $O(N^3)$ 的做法應該不難,直接枚舉任三條直線,再用任意一種求三角形面積公式加起來。於是若要加速應該會去想看看加起來的過程是否有一些步驟可以一併處理。

三角形面積的求法對於解題來說是很重要的。若你訪問一個人要求三角形面積要怎麼算,國中小學生大概會回答你底乘以高,高中生可能會回答你海龍公式、$a \cdot b \cdot sin(theta)/2$、兩向量外積除以二等。而在競賽中的計算幾何,兩向量外積應該是最常用的,但也有一個大家也很常用甚至適用於求任意簡單多邊行面積的方法:

把多邊形每個點想像成一個由原點到該點的向量,其中一個逆時鐘的順序是 $v_1 \sim v_N$,則此多邊行面積將會是 $\sum_{i=1}^N v_i \times v_{i+1}$ (其中 $v_{N+1} = v_1$,$\times$是外積的意思)

這個做法絕大部分有關計算幾合的書都會提到。既然有一個這麼常用到的公式,從這裡下手也無可厚非吧?把這個公式和這題聯想在一起,我們會發現,所有有外積運算的點對都會同時在題目給定的某條直線上。每條直線上將有 $N-1$ 個我們關心的點,這些點共有 $O(N^2)$ 個外積運算,所以我們只要把這些外積運算做到 $O(N log N)$ 甚至是 $O(N)$ 就解決這題了!

要如何加速運算,常見的著眼點就是用一個好的順序處理這些點,使得每次加入一個新的點時,可以用數學或資料結構的方式快速求出和已存在的所有點的外積。我賽中想到的方法,就是使用所有點在該直線上按照此直線向量的方向來決定點的加入順序,再套一些資料結構來做。

但實際上,順序若採用產生交點的直線斜率來排序,將會有更好的性質!(所以在想這類問題時,要多想想各種排序方式,可能會發現更簡單的做法 0.0)

請參考此程式碼。(若有什麼不懂得 function 或運算操作,請務必搞懂他們 >_< 這份 code 超短 !) (補上此奮程式碼賺寫者親自解釋做法的 comment)

我簡單描述這份程式碼在做啥。

1. 把所有直線按照斜率排序。此過程時間複雜度為 O(N log N)。
2. 枚舉直線 $L_i$,按照斜率考慮此直線和其他所有直線的交點,也就是說其他直線是由和他夾角最小的開始枚舉到最大的。
3. 接著重點來了,對於任意三條直線$L_i$、$L_j$、$L_k$,若 $L_j$ 和 $L_i$ 的夾角比$L_k$ 和 $L_i$ 的夾角小,那麼這三條直線所形成的三角形的三個頂點,按照逆時鐘排列,一定是 $L_k$ 緊接在 $L_j$ 之後。簡單來說就是任兩點外積的順序和點加入的順序都一樣
於是我們要求的就是 $\sum_{i < j} \bf{p_i*p_j}$ ($\bf{p_i}$ 是第 $i$ 個加入的點所代表的向量),要把這個加總做到 O(n) 應該不困難,可參考程式碼。

由角度的順序加點和按照交點在該直線上的順序加點最大的不同就是,不用在另外考慮兩點在外積時誰左誰右,所以做法就變簡單很多。

在附另外一個我覺得比較奇葩的做法,請參考此程式碼

這份程式碼相對前一份來說並不好懂(因為很奇葩?)。

奇葩的部分就是他算三角形面積的方式是非常少見 (至少我沒有看過的印象)。

要求三條直線所產生出來的面積,他先另外取一條不和這三條線平形的直線 $L_0$ (你可以看到程式碼中他採用旋轉所有 $N$ 條線一個奇怪的角度,然後另外取的線就直接取 $x$ 軸),然後把這三條線按照和 $L_0$ 的夾角排序,令其分別是 $L_1$、$L_2$、$L_3$,所求面積將等於 ($L_0$、$L_1$、$L_2$三角形面積) $+$ ($L_0$、$L_2$、$L_3$三角形面積) $-$ ($L_0$、$L_1$、$L_2$三角形面積)

用下圖來舉例的話,就是我們要求的黃色部分面積是 (黃+紫) + (粉) - (紫+粉)。

然後大家請不要以為看這張圗對了就覺得這樣算面積的方法一定是對的,實際上我至今還不知道怎樣簡單的證明這個面積算法的正確性,目前我只會使用枚舉高達十幾種 case 來證明 ...

有了這個求三角形面積的方法後,我們就先把所有直線按照和 $L_0$ 的夾角排序,之後在枚舉任兩條直線,計算他們和 $L_0$ 所形成的三角形面積用 $O(1)$ 在計算中有幾次被當成正的,有幾次被當成負的(可參考 code),這題就完成了!

problem D --- Fuzzy Search

題意:給兩個僅包含字母 ATCG 的字串 $S$ 和 $T$ 以及一個非負整數 $k$,$T$ 的長度不超過 $S$,問$t$有幾種和$s$的位置配對方式,使得 $T$ 中每個字母和 $S$ 中最近的相同字母位置差絕對值不超過 $k$。

數據範圍:$字串長度不超過 $200,000$,$0 \leq k \leq 200,000$

tag:[ 傅立業 ]

= = = = = = = = = = = = = = =
//以下這段和做法沒關係
這題比賽中我有想了一陣子有沒有普通的字串匹配解 (我覺得這題若用傅立業太無趣了 ... 最近的一些題目都給你用傅立業就好了啊 = =),但最後還是決定回歸傅立業硬爆,但我發現我沒有把 FFT 放在我的 codebook 資料夾裡 ...,只有一份 NTT 的 code,而且在我地記憶中我的 NTT codebook 超慢...,不過賽中我還是硬著頭皮上了, submit 了兩次,第一次 RE,就想說我陣列或許開太小了(咦我以為我有好好的計算過),於是就在把陣列開兩倍大再上傳,結果得到 TLE OAQ,這題有三秒耶!而且 $n$ 只有 $20000$ 耶!連這樣也TLE...?,這時候我就在思索說,究竟要現場壓一下這份 NTT 的常數,還是回去挖以前 ICPC 組隊時對有用的 FFT 出來?而就在這個時候,Codeforces 公告說,這題的測試資料數據給的比題目所說的數據還大,晚點會 rejudge (難怪第一份會 RE...)。此時我賭性堅強,就決定放著不管啦,最後這題很幸運的以差 0.005 秒的時間獲得 AC,但真的能稱做很幸運嘛?這只能怪我沒有打裡好自己的codebook 吧?在這種人手一份 FFT codebook的時代,大家幾乎都不是親自寫 FFT,而你確 TLE 在不是親手寫的部分,那只能怪自己事先準備不充分了 ...
//以上這段和做法沒關係

只有ACTG四種字母,很容易讓人聯想到每種字母分開考慮,接著來想想看對於一種字母來說,會造成哪些位置不能擺?再問更明確些,如果 $T$ 的第 $i$ 個位置是 'A' 那 $T$ 的開頭不能擺在哪些位置呢?如給定 $i$,應該很容易想到一個 $O(|S|)$ 的做法解決這個問題吧?(應該只是個Div. 1 A難度的問題) 而且也不難發現,對於不同的 $i$ 值,不能擺的位置只是隨著 $\bf{i}$ 的增加向反方向位移而已,若觀察到粗體字的部分,應該就會馬上聯想到 FFT 了吧?所以由這樣的思路看來,這題應不難,以 AC 數量來看他難度和 pC 差不多,他會被排在 pD 就只是因為他用了 FFT。

並且由於我們只關心 FFT 的結果是否非零,所以他的摺積運算可以使用位元運算加速,所以也有很多人是使用位元運算來加速的,一直以來,只關心是否非零的傅立業題都卡不掉位元運算...(例如 CF472G),請看看別人怎麼用位元運算 AC本題,看了後你一定會感嘆說為什麼要去使用 FFT ...。

Problem C --- Data Center Drama

題意:給一個 $n$ 個點 $m$ 條邊圗(可以有重邊和自連邊),請加盡量少的邊並把所有邊定向,使得每個點的 in degree 和 out degree 都是偶數。

數據範圍:$1 \leq n \leq 100,000$,$1 \leq m leq 200,000$

tag:[ 圗論 ] [ 尤拉路徑 ]

= = = = = = = = = = = = = = =
我一讀完題目就連想到本年度台大 PK 賽的 pB --- 給一個圖把他定向,使得所有點的 in degree 和 out degree 的差的絕對值不超過 1。想必這題也是個類似做法(某種程度上把degeree變成偶數,然後在尤拉路徑上做一些構造) 構造題。於是我就立刻想到如果所有degree都是偶數,而且邊數也是偶數且連通時,只要按照任意尤拉路徑的順序,把邊定向為一正一反就能滿足條件了。
接下來就一個一個把粗體字的條件去掉看看要怎麼辦~。若 degree 不是偶數一定得變成偶數才有可能能滿足題目條件,那就兩個兩個奇點(奇數 degree 的點)來配對,但任意一種配對方式都可以嗎@@並不是,實際上必須讓配對後連通塊的數量變愈少愈好,畢竟若之後有某個連通塊若只有奇數個邊,那就還得再增加邊,而若一剛開始就先把很多個有可能變成奇數個邊的連通塊串起來,想必能減少要增加的邊的數量,至於要怎麼把所有含有積點的連通塊串起來呢~這裡就不說了 >_<。最後我們把每個仍含奇數個邊的連通塊隨便找一個點加一條自連邊,就能滿足所有粗體條件了!

上一段只是大略講一下我的思路,至於很多細節還是得好好的證明。

code 可以參考這裡。然後我看到這份 code 才發現題目有圗是連通的這個條件.... 所以這題更簡單了 Orz 都怪我只大略看了題目敘述以及看了所附的圗及 Input 就猜題意了... 但圗是連通的這個條件怎麼可以不寫在 Input 裡啦 Orz

另外還有一種不需要由拉路徑的有 tree dp 感的做法,請參考 this comment

Problem B --- Clique Problem

題意:數線上有 $N$ 個點,每個點都有權重,且任兩個點若權重和不超過兩個點距離時,就有邊連接這兩個點,請求出最大的點團,使得此點團任兩點都有邊相連。

數據範圍:$N$ 不超過 $200,000$,座標介於 $0 \sim 10^9$且兩兩互不相同,權重介於 $1 \sim 10^9$。

tag : [ greedy ] [ dp ]

= = = = = = = = = = = = = = =
這題畫個圗應該就可以發現,此題等價於,在眾多 $[x_i - w_i, x_i + w_i]$ 的區間中,要選盡量多的區間使得讓兩區間交集長度不大於 $0$。想到這裡後就令我覺得,這隨便 dp 都能 AC 吧?於是就憑直覺寫了一個有點稍嫌複雜的 dp。

可是後來看到別人的 code 後整個崩潰 ...

若你也寫了個有點複雜的 dp,去看一下Editorial你就會崩潰了。

題意:有 $w*h$ 大小的蛋糕,有 $n$ 個操作,每個操作會從水平或垂直的某個位置切一刀,請問每個操作後最大的那塊單高面積。
數據範圍:$2 \leq w, h \leq 200,000$,$1 \leq n \leq 200,000$,不會有重複的操作。
tag : [ STL ] [倒著做 ]

= = = = = = = = = = = = = = =
這題以眼就令人覺得是經典的STL模擬聯習題,有些人會因為不熟悉 STL 的用法而沒AC,例如說 set $S$ 要取lower_bound,必須使用 S.lower_bound(x),但有人卻因為使用 lower_bound(S.begin(),S.end(),x) 而獲得了TLE。大家若好好讀熟 STL的使用方式相信對比賽中有一定的幫助 0.0

這題比較值得一題的是,若想像成先一次做完所有操作,再把操作一個一個倒著取消回來,將可以做到 O(n)。