1. 程式人生 > >[CTSC2008]圖騰totem

[CTSC2008]圖騰totem

(圖騰這題做的我頭疼 233)

記 f(xxxx) 為 xxxx 出現的次數,那麼題目就是要求 f(1324) - f(1243) - f(1432)

最有難度的是把上面的式子轉化一下,變成 f(1x2x) - f(14xx) - f(12xx) + f(1234)

這點除非對 f 的求法能一眼看出來,否則很難想到

後兩個比較簡單一點

f(1234) 可以想到用動規求

具體來講,設 dp[i][j] (i≤n, 1≤j≤4) 表示第 i 個數後面接 j 個上升序列

這樣轉移就是 dp[i][j] = dp[k][j] + dp[k][j-1],其中 k 是一個滿足 a[i] < a[k],i < k 的下標,k 可以用線段樹求

然後就是求 f(12xx)了

利用類似的求法,首先維護一下每個點右邊有多少點大於自己,這點用樹狀陣列即可

然後維護一下上面的字首和,這樣就可以計算 f(12xx) 了

也就是用兩層樹狀陣列

至於 f(1x2x) 的求法理解起來並不難

按照高度從小到大把每個點放到原序列中去,不妨設當前點是 2

可以確定空位置比當前點大,而且當前點比之前放入的點大

那麼為了使用計數原理需要求出空位置的數量

既然之前的點都比當前點小,假設左邊點的下標集合為 S,當前點為 j

對於 S[i],我們希望知道 S[i] 到 j 有多少空位置

聯想到區間和,又可以用樹狀陣列啦

當然 |S| 可能會很大,既然這樣乾脆再套一層樹狀陣列得了

這樣就求出 f(1x2x) 了

f(14xx) 就變得玄學一點了

 利用類似上面的思想,把原序列從左到右放入一個排好序的序列

假設當前的是 4,那麼排序序列裡 4 左邊的數都是 1

中間的空白就是原序列後面的 xx

然後假設 1 的下標是 i,4 的下標是 j,需要求 (j-i) * (j-i-1) / 2

也就是 (j-i) * (j-i) - (j-i)

令 r[i] 表示下標為 i 的數右邊空白的數量

那麼上面的式子就是 (r[i] - r[j]) ^ 2 - (r[i] - r[j])

拆開, r[i] ^ 2 + r[j] ^ 2 - 2 * r[i] * r[j] - (r[i] + r[j])

考慮左邊的所有 1,那麼要求的就是 sum { r[i] ^ 2 - r[i] } - sum { r[i] } * r[j] * 2 + ( r[j] ^ 2 - r[j] ) * ( #1 )

於是維護 r 與 r ^ 2 的區間和就好了

這題就這樣解決啦