# b167: 差分
### 提敘
如題,就是差分。
### 考點
差分。
不放程式碼跟解講了,就完全是[講義](https://hackmd.io/@Cm2WYhkESReDzk7RvyH3Ww/SkUCY8nqC/https%3A%2F%2Fhackmd.io%2F%40PoteLiu%2FByR87A2Y1e)的東西,然後改變一下輸出的方式。
# b197: Pote Liu 冒險外傳 (2)
### 提敘
經典的空間分割,平常在寫數學應該也會遇到。
### 考點
考的是遞迴,我給了很多項,所以你就算不會寫也應該會把通式求出來吧。
啊我就不放遞迴式了,放了就等於給 $AC \; code$。
###### 然後我還是很想去故宮南院,下次到南部應該是畢旅,不知道有沒有機會。
# b205: 分子生物學的中心法則 (1)
### 提敘
就是在說 DNA 如何複製。
### 考點
其實我覺得我這題跟 `文字獄系列` 沒什麼差,只是你找到起點後要開始進行轉換,然後要記得最後要把字串反轉。
找到起點跟終點。
```cpp=
if(dna.find(oric) != string::npos && dna.find(ter) != string::npos){
start = dna.find(oric) ;
finish = dna.find(ter) ;
}
```
至於為什麼要用到 `string::npos` ,請去看[講義](https://hackmd.io/@Cm2WYhkESReDzk7RvyH3Ww/SkUCY8nqC/https%3A%2F%2Fhackmd.io%2F%40PoteLiu%2FBJZZ_x93R#find)。
接下來我的作法是把該段字串複製下來。
```cpp=
string target = "" ;
for (int i = start ; i < finish + ter.length() ; i++) {
target += dna[i] ;
}
```
然後開始轉換,一樣再建一個字串然後進行判斷,我就不放了,再放乾脆直接給 $AC \; code$。
# b204: 分子生物學的中心法則 (2)
### 提敘
就是在說 DNA 如何轉錄。
### 考點
同上,不過 $A→T$ 變成 $A→U$。
所以這就是水題,因為跟上面那題一樣,你解一題可以拿兩題分數,超賺。
# b176: Pote Liu 冒險日記 (2)
### 提敘
好,這題的確題序有點長,因為我在出這題的時候是剛放寒假很閒。
他要你輸入一個二維陣列,然後再進行一些判斷。
### 考點
本題有提到一個名為**曼哈頓距離**的東西。
如果你有玩過 Mincraft 就知道他就是水的流動方法。
如果你有寫歷屆的 APCS 題目的話,你會知道這東西就是 [APCS 2024 6月的第二題 -- 電子畫布](https://zerojudge.tw/ShowProblem?problemid=o077)。
你會發現他的值是向外遞減,所以你可以知道我的邊界如下。
```cpp=
xg - g ;
yg - g ;
```
不過這樣你只能拿到最外圍的,所以很明顯你需要遍歷這個二維陣列,所以你會得到這個。
```cpp=
nxi = max(0, xg - g) ;
nxf = min(m, xg + g + 1) ;
nyi = max(0, yg - g) ;
nyf = min(n, yg + g + 1) ;
```
`nxi` 和 `nxf` 確定要遍歷的 `x` 範圍,不能超出界限 `[0, m)`。
`nyi` 和 `nyf` 確定要遍歷的 `y` 範圍,不能超出界限 `[0, n)`。
接著就在迴圈遍歷,曼哈頓距離公式是 $d(x,y) = |x_{1}-x_{2}|+|y_{1}-y_{2}|$ 。
把每一點跟 `(xg , yg)` 比,再把值放到陣列裡,記得值最小最小只會是 $1$ 。
然後你就把二維陣列的值建好了,接下來就是判斷輸入的該點需不需要躲避,結束。
# b207: 功德成聖啦 !
### 提敘
有 $n$ 間房子連在一起,一直往前走,每一個編號為 $i$ 的房間有 $p_i$ 個量的功德。
要分 $m$ 次把他的境界升到金仙中期,每次要蒐集 $q_i$ 個量的功德,從第一個進去的房間走到第 $(t+1) \; mod \; n$ 個房間,問最後會在哪裡。
### 考點
首先你會發現旁邊的時間被壓成 $0.1s$,你會知道這看起來是需要一些特殊的方法來做。
然後你看到他的配分分成兩個,還是特別奇怪的數字($2∗10^5$),就更加確定了。
根據內容敘述會發現他要用到陣列的和,這代表你要做前綴和。
做完之後你驚訝的發現他具有**單調性**,又看到他在詢問位置,所以你知道你需要做二分搜。
```cpp=
int left = 0 , right = n ;
while(left < right){
int middle = (left + right) / 2 ;
if(target > pre[middle]) {
left = middle + 1 ;
}
else {
right = middle ;
}
}
ans = left ;
```
最後記得要把 $ans \; mod \; n$ 。
# b203: 分子生物學的中心法則 (3)
### 提敘
就是在說 mRNA 如何轉譯。
### 考點
比較麻煩的一點是需要建表,可以用 `vector` 配 `pair` 但我是用 `unordered_map` (只是因為方便一點)
避免各位打到不爽我在這放表。(可能有其他方式但我只有這樣做。)
```cpp=
vector<pair<string, string>> codonlist = {
{"AAA", "000001"}, {"AAU", "000010"}, {"AAC", "000011"}, {"AAG", "000100"},
{"AUA", "000101"}, {"AUU", "000110"}, {"AUC", "000111"}, {"AUG", "001000"},
{"ACA", "001001"}, {"ACU", "001010"}, {"ACC", "001011"}, {"ACG", "001100"},
{"AGA", "001101"}, {"AGU", "001110"}, {"AGC", "001111"}, {"AGG", "010000"},
{"UAA", ""}, {"UAU", "010001"}, {"UAC", "010010"}, {"UAG", ""},
{"UUA", "010011"}, {"UUU", "010100"}, {"UUC", "010101"}, {"UUG", "010110"},
{"UCA", "010111"}, {"UCU", "011000"}, {"UCC", "011001"}, {"UCG", "011010"},
{"UGA", ""}, {"UGU", "011011"}, {"UGC", "011100"}, {"UGG", "011101"},
{"CAA", "011110"}, {"CAU", "011111"}, {"CAC", "100000"}, {"CAG", "100001"},
{"CUA", "100010"}, {"CUU", "100011"}, {"CUC", "100100"}, {"CUG", "100101"},
{"CCA", "100110"}, {"CCU", "100111"}, {"CCC", "101000"}, {"CCG", "101001"},
{"CGA", "101010"}, {"CGU", "101011"}, {"CGC", "101110"}, {"CGG", "101111"},
{"GAA", "110000"}, {"GAU", "110001"}, {"GAC", "110010"}, {"GAG", "110011"},
{"GUA", "110100"}, {"GUU", "110101"}, {"GUC", "110110"}, {"GUG", "110111"},
{"GCA", "111000"}, {"GCU", "111001"}, {"GCC", "111010"}, {"GCG", "111011"},
{"GGA", "111100"}, {"GGU", "111101"}, {"GGC", "111110"}, {"GGG", "111111"}
};
```
然後步驟也是一樣,找到起點,開始**一格一格**跑,不是三格三格。
建一個字串放編好的字串,一格一格編直到找到終點。
關於找終點,這裡有一個好用的函式。
```cpp=
string codon = mRNA.substr(i, 3) ;
```
然後就可以輸出了。
# b195: 你就是我們班的 Spy 啊 !
### 提敘
對不起沒有重點。
### 考點
因為這題要敘述真的好難敘述,而且難發現規則,所以請看提示的圖片。

還是要感謝 chrislaiisme,這題也是在他每日一題的程式裡發現的。
我覺得這題真的很精妙,首先你看完圖片會發現他有規律($2m$),因此你開的陣列會是這樣。
```cpp=
vector<int> arr(m * 2 + 1 , 0) ;
```
你會有一個問題是該如何轉向,從他轉向是轉**某一段**的陣列可以發現,他就是要改變某一段的值,所以你可以想到要做差分,只需要改變重要的點,其他中間會透過前綴和累積出來。
```cpp=
while(n--){
int p ;
char d ;
cin >> p >> d ;
height += p ;
if(p == 0) d = 'U' ;
if(p == m) d = 'D' ;
if(d =='U'){
arr[0] ++ ;
arr[m-p] -= 2 ;
arr[m-p+m] += 2 ;
}
else if (d =='D'){
arr[0] -- ;
arr[p] += 2 ;
arr[p+m] -= 2 ;
}
}
```
然後你就可以做第一次的前綴和,代表把每一格的變化量累積成目前的傾斜。
而第二次前綴和是代表把傾斜累積成高度。
最後在遍歷所有高度,找出最大值。
###### 這題真的超漂亮的。
# b188: Pote Liu 冒險日記 (3)
### 提敘
找二維中最大子矩陣和。
### 考點
如果你寫過 Vandrin 的最大區間和,你就會發現其實這題他非常像,只是變成二維了。
所以他的做法就是先把二維陣列用前綴和壓成一維陣列。
```cpp=
for (int i = 1 ; i <= n ; i++){
for (int j = 0 ; j < m ; j++){
cin >> pre[i][j] ;
pre[i][j] += pre[i-1][j] ;
}
}
```
最外層兩個迴圈是固定位置,然後看成一維陣列做最大區間和。
```cpp=
for (int i = 1 ; i <= n ; i++ ){
for (int j = i ; j <= n ; j++){
dp = pre[j][0] - pre[i - 1][0] ;
for (int k = 1 ; k < m ; k++) {
val = pre[j][k] - pre[i - 1][k] ;
dp = max(dp + val, val) ;
ans = max(ans, dp) ;
}
}
}
```
# b196: 成為合格商人的第一課
### 提敘
找出一個可以以最小的損失賺到最多錢的方法。
### 考點
如果看不懂的話,畫出來你就會發現,這就是最大矩形面積, `stack` 經典題目。(啊我上課問你們要不要講好幾次都說不要。)
所以你要從左找比自己矮的右邊 ; 從右找比自己矮的左邊。
用 `stack` 從左到右掃,如果 `stack` 頂端比現在的高度高或一樣就 `pop` ,因為這個比自己高,不能當界線。
右邊同理,這裡不放了。
```cpp=
for (int i = 0; i < n; i++) {
while (!stk.empty() && heights[stk.top()] >= heights[i]) {
stk.pop();
}
left[i] = stk.empty() ? 0 : stk.top() + 1;
stk.push(i);
}
```
然後把每一根柱子嘗試當作最矮的高度進行遍歷,就是 高度 $*$ 寬度 取最大值。