---
# System prepended metadata

title: 複習與題目

---

# 複習與題目
我已經不知道可以講什麼了 最後一次資讀就讓我偷懶一下
[cses](https://cses.fi/problemset/list/)
[usaco guide gold](https://usaco.guide/gold/)
## 數學
### 講義
#### [模逆元](https://hackmd.io/@Viecon-342524/SJzSuO_Bj#%E6%A8%A1%E9%80%86%E5%85%83-Modular-Multiplicative-Inverse)
#### [其他數學](https://hackmd.io/MGfghW7KQu2MdWTOzM6WcQ?view#-Math)
### 題目
#### 模逆元
[Exponentiation II](https://cses.fi/problemset/task/1712)

$$
\begin{aligned}		
a^{b^c}&\equiv a^{k(p-1)}a^{b^c-k(p-1)} (\text{mod}\, p)
\\&\equiv a^{b^c-k(p-1)}
\end{aligned}
$$

#### 排列組合
大部分直接代公式就好
[Bracket Sequences II](https://cses.fi/problemset/task/2187)
畫方格然後把不合法的映射到對稱的地方就可以扣掉了

#### 因數
[Common Divisors](https://cses.fi/problemset/task/1081)
對值域做事
[Sum of Divisors](https://cses.fi/problemset/task/1082)
不難發現答案為$\sum_{i=1}^n i\lfloor {n\over i}\rfloor$
![](https://hackmd.io/_uploads/Sy4bSZMP2.png)
把$\lfloor {n\over i}\rfloor$一樣的一起做，複雜度為$O(\sqrt n)$
## 字串
### 講義
#### [Hash](https://hackmd.io/IKxtR4cLTwasYZPLFc7Q_w#Hash)
#### [string technique](https://hackmd.io/-5rjlBbuSeq6YpRm12XA7A)
### 題目
#### hash
[Finding Periods](https://cses.fi/problemset/task/1733)
窮舉長度，然後用前綴和的想法去算hash
[E. Compress Words](https://codeforces.com/contest/1200/problem/E)
暴力
#### trie
[Word Combinations](https://cses.fi/problemset/task/1731)
由後往前，如果前綴有在trie裡(並且要是結尾)就更新dp
:::spoiler code:
```cpp=
#include<bits/stdc++.h>
using namespace std;
int mod=1e9+7;
vector<int> dp;
struct Trie{
    Trie* T[26];
    int cnt;
    Trie(){
        cnt=0;
        memset(T,0,sizeof(T));
    }
};
Trie *t;
void insert(string s){
    Trie *ptr=t;
    for(char i:s){
        if(!ptr->T[i-'a']){
            ptr->T[i-'a']=new Trie();
        }
        ptr=ptr->T[i-'a'];
    }
    ptr->cnt++;
}
void query(string s,int pos){
    Trie *ptr=t;
    int l=1;
    for(char i:s){
        if(!ptr->T[i-'a']){
            return;
        }
        if(ptr->T[i-'a']->cnt>0){
            dp[pos]+=dp[pos+l];
            dp[pos]%=mod;
        }
        l++;
        ptr=ptr->T[i-'a'];
    }
}
int main(){
    string s;
    int n,k;
    cin>>s>>k;
    n=s.size();
    dp.resize(n+1);
    dp[n]=1;
    t=new Trie();
    for(int i=0;i<k;i++){
        string a;
        cin>>a;
        insert(a);
    }string tmp="";
    for(int i=1;i<=n;i++){
        tmp=s[n-i]+tmp;
        query(tmp,n-i);
    }
    cout<<dp[0]%mod<<endl;
}
```
:::
## dp
### 講義
[Dynamic Programming II](https://hackmd.io/@Viecon-342524/ryjjol4Ns)
### 題目
#### 有限背包
[Book Shop II](https://cses.fi/problemset/task/1159)
令$dp[i][j]$為考慮前$i$個物品且重量為$j$的方法數，則
$$
\begin{aligned}
dp[i][j] &= \max_{0 \leq num \leq x_i}(dp[i - 1][j - num * w_i] + num * v_i)
\\&= \max_{k \equiv j \pmod {w_i}, 0 \leq \frac{j - k}{w_i} \leq x_i}(dp[i - 1][k] + v_i * \frac{j - k}{w_i})
\\&=v_i * \lfloor \frac{j}{w_i} \rfloor + \max_{k \equiv j \pmod {w_i}, 0 \leq \lfloor \frac{j}{w_i} \rfloor - \lfloor \frac{k}{w_i} \rfloor \leq x_i}(dp[i - 1][k] - v_i * \lfloor \frac{k}{w_i} \rfloor)
\end{aligned}
$$
只有和$j$同餘的會有可能是轉移點。
然後維護一個單調隊列，每次從$x_i$的範圍中找最大的


另一種方法:
把東西分成$2^0, 2^1, 2^2$個
## 分治
把東西切小塊的都是分治，包含二分搜、線段樹等。
### 題目
#### 線段樹與bit的應用
[Subarray Sum Queries](https://cses.fi/problemset/task/1190)
要存的資訊:sum、最大prefix、suffix、subarray
```cpp=
void pull(){
    sum=lc->sum+rc->sum;
    pre=max(lc->pre,lc->sum+rc->pre);
    suf=max(rc->suf,rc->sum+lc->suf);
    mx=max({lc->mx,rc->mx,lc->suf+rc->pre});
}
```
[Intersection Points](https://cses.fi/problemset/task/1740)
對x的值域開bit(或線段樹)，把所有線段依y座標排序，就可以轉換成區間加值和區間求和了(或者單點加值區間求合)。
#### 其他
[Meet in the Middle](https://cses.fi/problemset/task/1628)
從中間切一半，枚舉集合，把可能的合丟進vector裡，sort，然後再二分搜找有哪些可以合成$x$。
[E. Maximum Subsequence](https://codeforces.com/contest/888/problem/E)
就和上面那題差不多。
[F. Xor-Paths](https://codeforces.com/contest/1006/problem/F)
把方格從右上到左下(原因是每條路徑都一定只通過這條線一次)切一半，線上每個點都存從起點走和從終點走可能有哪些值，一樣可以二分搜。
:::success
:bulb: 若$a\oplus b=c$ 則 $b\oplus c=a$
:::