# 複習與題目 我已經不知道可以講什麼了 最後一次資讀就讓我偷懶一下 [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$ :::