# TFcis t27 寒訓 + 台南一中 115&116資訊讀書會 練習賽 (div 1) 題解
## pA - 爬樓梯 2.0
**author : blame**
**problem setter : blame**
**first kill : 聖殤**
:::spoiler **subtask 1 (5/100) 1 <= N <= 2 × 10^5**
**div 2的 ac code**
:::
:::spoiler **subtask 2 (10/100) M = 0**
**算數學**
:::
:::spoiler **subtask 3 (12/100) M = 1**
**分成兩半,算數學**
:::
:::spoiler **subtask 4 (100/100)**
**$N$ 看起來就一臉超大的樣子**
**然後爬樓梯又是經典的DP問題**
**所以我們可以馬上想的到DP優化中的 "矩陣快速冪" 幫助轉移**
**然後經過一連串的實作就可以AC了**
:::
## pB - APMO初選第1.5階段
**author : paul**
**problem setter : blame**
**first kill : 聖殤**
:::spoiler **subtask 1 (1/100)**
$$
mn = c
$$
**所以只要找 $C$ 的因數個數 $\times 2$ 就行**
:::
:::spoiler **subtask 2 (100/100)**
$$
mn = a \times m + b \times n + c
$$
$$
mn - am - bn = c
$$
$$
m(n-a) - bn = c
$$
$$
m(n-a) - bn + ba = c + ba
$$
$$
m(n-a) - b(n-a) = c + ab
$$
$$
(m-b)(n-a) = c + ab
$$
**所以簡單的找 $c + ab$ 的因數個數 $\times 2$ 即可**
```cpp=
#include <iostream>
using namespace std;
int frac[1000001];
int main() {
for(int i = 2; i <= 1000000; i++)
for(int j = i; j <= 1000000; j+=i)
frac[j]++;
int t; cin >> t;
while(t--) {
int a, b, c; cin >> a >> b >> c;
cout << frac[c+a*b]*2+2 << '\n';
}
}
```
**複雜度 $O(10^6lg(10^6) + Q)$**
:::
## pC - 快速冪
**author : blame**
**problem setter : blame**
**first kill : wonderhoi**
:::spoiler **subtask 1 (1/100) 1 <= A, B <= 10^9**
**正常的快速冪,只是要記得 $1 \le C \le 10^12$**
**所以在快速冪相乘的時候會溢位 超出 long long 範圍**
:::
:::spoiler **subtask 2 (2/100) 1 <= B <= 10^9**
**發現 $A$ 已經屬於大數範圍了**
**所以要對 $A$ 做大數取模,取模對象是 $C$**
:::
:::spoiler **subtask 3 (3/100) C 是質數**
**對 $A$ 以 $C$ 做大數取模, $B$ 以 $C-1$ 做大數取模**
:::
:::spoiler **subtask 4 (100/100)**
**發現 $C$ 不是質數了,所以要使用歐拉函數**
**$A^{phi(C)} = 1 \mod C$**
**然後就AC了**
**只是大家都卡在 $int128$**
:::
## pD - 快樂線段樹2.0
**author : blame**
**problem setter : blame**
:::spoiler **subtask 2 (100/100)**
**這題其實應該要有更細一點的配分**
**但是因為來不及,所以就沒有配分了**
**看到區間改值跟區間詢問,第一件事情會想到 "線段樹"**
**而後看到題目說線段樹**
**所以猜這題不是線段樹**
**然後你就猜對了**
**這題是柯朵莉樹**
**普通的科朵莉樹足以解掉這題**
:::
## pE - 區間平方和
**經典問題**
**problem setter : blame**
:::spoiler **subtask 1 (33/100) subtask 2 (100/100)**
**subtask 1 差不多正解,只是差在線段樹的 build 函式跟常數優化而已**
**首先會發現區間改值跟普通的懶標線段樹差不了多少**
**但是現在詢問的是區間平方和ㄟ?**
**國中就會的數學:**
**$(a + b)^2 = a^2 + 2 a b + b^2$**
**所以我們只要維護當前區間總和 跟 答案即可**
**當然,會有人想用zkw來解這題**
**但 zkw 似乎做不到這件事情,可以想一下為什麼**
:::
## pF - 歷史老師的安全帽
**author : brinton**
**problem setter : brinton**
:::spoiler **subtask 3 (5/100)**
```cpp=
#include <iostream>
using namespace std;
int main() {
cout << "0\n";
}
```
:::
:::spoiler **ac**
問asdfg1021
:::
## pG - 一堵很高很大的牆
**author : brinton**
**problem setter : brinton**
:::spoiler **subtask 3 (10/100)**
**1直接輸出0**
**3直接輸出3**
**2的話看看有沒有跟3被1隔開,沒有就過是0,否則為1**
:::
:::spoiler **subtask 2 (15/100)**
**暴力解**
**對於每個比自己大的,去求他與自己的低谷值,取最小的**
**實作上可以每次遇到更小的,就把比自己大但還沒更新的更新上去**
:::
:::spoiler **subtask 4 (100/100)**
**滿分解**
**關鍵觀察0:若 $(A \to B)$ 的低谷值 $=(B \to A)$ 的低谷值**
**假設我們已知 $A \to B$ 的低谷值,$A \to C$ 的低谷值,且 $A > B > C$,我們想求 $C$點的神秘值。**
**關鍵觀察1:若 $(A,B)$ 的低谷值為 $x$, $(A,C)$ 的低谷值為 $y$,則 $(B,C)$ 的低谷值至少 $\geq min(x,y)$**
**$proof:$**
**因為 $C-B$ 存在一條 $C \to A \to B$ 的路**
**那 $C$ 點的神秘值能不能寫成 $max(0,C-min(x,y))$?**
**有沒有可能 $B \to C$ 存在更大的低谷值呢?**
**我們這邊只考慮 $min(x,y) \lt C$
不然答案都是 $0$**
**如果 $y \le x$ 那答案是對的(答案由y決定)**
**如果 $x \lt y$ 且 $x < C$,因為 $x >= min(A->C,C,C->B)= min(y,C,C \to B)$,又 $y$, $C \gt x$,故 $C \to B \le x$**
**所以就只要做所有點跟其中一個最高點的低谷值,然後填答案由大到小填,答案為 $max(0,C-min($這點到最高點的低谷值,所有比他大的點和最大點的低谷值$))$**
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define inf (long long)(1e15)
int di[4] = {1,0,-1,0};
int dj[4] = {0,1,0,-1};
signed main(){
cin.tie(0);
ios_base::sync_with_stdio(0);
//start here
int N,M;
cin >> N >> M;
vector<vector<int>> grid(N,vector<int>(M));
vector<array<int,3>> vp;
for(int i = 0;i < N;i++){
for(int j = 0;j < M;j++){
cin >> grid[i][j];
vp.push_back({grid[i][j],i,j});
}
}
sort(vp.begin(),vp.end());
reverse(vp.begin(),vp.end());
// start from high
priority_queue<array<int,3>> pq;
pq.push(vp[0]);
vector<vector<int>> dij(N,vector<int>(M,-1));
dij[vp[0][1]][vp[0][2]] = vp[0][0];
while(!pq.empty()){
auto [upd,pi,pj] = pq.top();
pq.pop();
if(upd != dij[pi][pj])continue;
for(int d = 0;d < 4;d++){
int ni = pi+di[d];
int nj = pj+dj[d];
if(ni < 0 || nj < 0 | ni >= N || nj >= M)continue;
if(dij[ni][nj] < min(upd,grid[ni][nj])){
dij[ni][nj] = min(upd,grid[ni][nj]);
pq.push({min(upd,grid[ni][nj]),ni,nj});
}
}
}
// for(auto i:dij){
// for(auto j:i)cout << j << " ";cout << endl;
// }cout << endl;
vector<vector<int>> ans(N,vector<int>(M));
int prev = vp[0][0];
int preMin = vp[0][0];
int cMin = vp[0][0];
for(auto [val,i,j]:vp){
if(prev != val){
preMin = min(preMin,cMin);
prev = val;
}
// cout << val << " " << i << " " << j << " " << preMin << " " << cMin << endl;
if(val == vp[0][0]){
ans[i][j] = vp[0][0];
}else{
// cout << i << " " << j << " " << min(preMin,dij[i][j]) << endl;
ans[i][j] = max(0LL,val-min(preMin,dij[i][j]));
}
cMin = min(dij[i][j],cMin);
}
for(auto i:ans){
for(auto j:i){
cout << j << " ";
}
cout << '\n';
}
}
```
:::