samson_chaechae
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
1
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
--- tags: 自主學習 --- # 自主學習-演算法學習(每周進度與成果) 高二34623趙炫翔 # 本計畫所編的全部學習筆記 - [線段樹](/9WMgzFs6RbCZ3LQ5NSf7QQ) - [BIT](/fH6_FqTnQb2BHEV6EhG3lQ) - [模運算](/564I9dxSTf6crJP95qRnlQ) - [模反元素/模逆元](/zlqbqmRxTiqprniwOmkH6Q) - [快速冪&矩陣乘法&矩陣快速冪](/f6LDVf4yTNy-b4vf7kbx9g) - [LIS&LCS](/96ZnugpZRxqkW7LARlrWhg) - [背包問題](/HWN_qcwZTum7G-qiWgV7DA) - [單調隊列](/_VjchRMtRSyTgMyBQmB8QA) - [DP優化](/dvHGezXpRu-SLAj7McPc4A) # 第一周-線段樹 ## 本周成果 1. 複習線段樹的基礎概念,並強化實作熟悉度 2. 在DanDanJudge,用線段樹解出a710 ## 本周實作進度 ### a710筆記(9/2) [題目](http://203.64.191.163/ShowProblem?problemid=a710) 我的想法是用線段樹存0和1,0就是這個位置原本的數被刪掉了,1就是數還在 而從線段數第1項加到第k項,和就是舊數列第k項的數在新數列的位置 相反的新數列第t項在舊數列中的位置,就是從線段樹第1項開始加,當加到和t一樣時,這時線段樹的編號就是t在舊數列中的位置 而這個和具有單調性 所以可以利用二分搜找到新數列中的第k個在原本數列的位置 時間複雜度$O(Qlog^2N)/Q=2\times10^5,n=2\times10^5$ ### DDJ a710筆記補充(9/6) 我在看到解題列表後發現有很多人的解題時間都是0.1秒,看起來不像$O(Qlog^2N)$的解法,看起來像$O(QlogN)$,在學長提點和我的思考後,我發現先從線段樹取出值,然後再拿來二分搜根本沒必要,在線段樹內遞迴時就可以判段進入的子節點有沒有可能包含要查詢的值,就像在線段數內二分搜一樣。 --- $O(Qlog^2n)$ :::info :::spoiler <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> using namespace std; int n, q; vector<int> seg(800005); vector<int> arr(200005); #define m (l + r) >> 1 inline void build(int l = 0, int r = n, int i = 1){ if(r - l == 1){seg[i] = 1;return;} build(l, m, i << 1),build(m, r, i << 1 | 1); seg[i] = seg[i << 1] + seg[i << 1 | 1]; } inline int query(int ql, int qr, int l = 0, int r = n, int i = 1){ if(qr <= l || ql >= r){return 0;} if(ql <= l && qr >= r){return seg[i];} return query(ql, qr, l, m, i << 1) + query(ql, qr, m, r, i << 1 | 1); } inline void upt(int idx, int val, int l = 0, int r = n, int i = 1){ if(idx < l or idx >= r){return;} if(r - l == 1){seg[i] += val;return;} upt(idx, val, l, m, i << 1) , upt(idx, val, m, r, i << 1 | 1); seg[i] = seg[i << 1] + seg[i << 1 | 1]; } # undef m inline int bs(int x){ int l = 0,r = n,p = -1; for(int i = 0; i < 20; i++){ int m = (l + r) >> 1; if(query(0, m) == x) p = m - 1, r = m; else if(query(0, m) < x) l = m + 1; else r = m; } return p; } int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin >> n >> q; for(int i = 0; i < n; i++) cin >> arr[i]; build(); int p = -1; while(q--){ int d ,k; cin >> d >> k; upt(bs(d),-1); cout << arr[bs(k)] << '\n'; } return 0; } ``` ::: ![](https://i.imgur.com/nNUCVIk.png) --- $O(Qlogn)$ :::info :::spoiler <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> using namespace std; int n, q; vector<int> arr(200005); vector<int> seg(800005); #define m (l + r) >> 1 void build(int l = 0, int r = n, int i = 1){ if(r - l == 1){seg[i] = 1;return;} build(l, m, i << 1), build(m, r, i << 1 | 1); seg[i] = seg[i << 1] + seg[i << 1 | 1]; } void upt(int idx, int l = 0, int r = n, int i = 1){ if(idx < l || idx >= r)return; if(r - l == 1){seg[i] = 0;return;} upt(idx, l, m, i << 1);upt(idx, m, r, i << 1 | 1); seg[i] = seg[i << 1] + seg[i << 1 | 1]; } int query(int val, int l = 0, int r = n, int i = 1){ if(val > seg[i] || val <= 0)return 1; if(r - l == 1 && val == seg[i])return l; return query(val, l, m, i << 1) * query(val - seg[i << 1], m, r, i << 1 | 1); } # undef m int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin >> n >> q; for(int i = 0; i < n; i++){ cin >> arr[i]; } build(); while(q--){ int d ,k; cin >> d >> k; upt(query(d)); cout << arr[query(k)] << '\n'; } } ``` ::: ![](https://i.imgur.com/u6rgJtZ.png) --- # 第二周 ## 本周預期進度 1. 複習BIT的基礎概念,並強化實作熟悉度 2. 在DanDanJudge,用BIT解出a710 ## 本周成果 ### DDJ a710筆記 解題想法和上禮拜$O(Qlog^2N)$一樣 只是這次我把1加到k的工具換成了BIT 時間複雜度$O(Qlog^2N)$ --- $O(Qlog^2N)$ :::info :::spoiler AC_code ```cpp= #include<bits/stdc++.h> using namespace std; int n, q; vector<int> vc(200005); vector<int> bit(200005); int lowbit(int x) {return (x & -x);} inline void build(){ for(int x = 1; x <= n; x++){ int k = x; while(k <= n){ bit[k] ++; k += lowbit(k); } } } inline void upt(int x){ while(x <= n){ bit[x] --; x += lowbit(x); } } inline int query(int x){ while(x){ return query(x - lowbit(x)) + bit[x]; } return 0; } inline int BS(int idx){ int l = 1, r = n+1; int m = (l + r) >> 1; for(int i = 0; i < 20; i++){ int mval = query(m); if(mval > idx){ r = m; } else if(mval == idx){ if(mval - query(m - 1) == 0){ r = m; } } else if(mval < idx){ l = m + 1; } m = (l + r) >> 1; } return m; } int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin >> n >> q; for(int i = 1; i <= n; i++){ cin >> vc[i]; } build(); while(q--){ int d , k; cin >> d >> k; upt(BS(d)); int i = BS(k); cout << vc[i] << '\n'; } } ``` ::: ![](https://i.imgur.com/O5t0rBw.png) --- # 第三周 ## 預計進度 BIT講義 線段樹講義 ## 講義 [線段樹](/9WMgzFs6RbCZ3LQ5NSf7QQ) [BIT](/fH6_FqTnQb2BHEV6EhG3lQ) # 第四周 ## 預計進度 學習快速冪 & 矩陣乘法 學習模運算和模逆元(不再計畫內,後來補的) 解DDJ a689 、TIOJ 2053、UVA 10870 ## 本周成果 ### DDJ a689筆記 [題目](http://203.64.191.163/ShowProblem?problemid=a689) 這題就是排列組合 只是這題的範圍非常大 所以需要一點模運算 我是先把1\~100000階乘和1\~100000階乘的模逆元都先存起來 然後計算字串重複的個數 然後再用域先處理好的階乘數乘上重複個數階乘的模逆元 (等同於除上重複個數階乘--->費馬小定理$a^{-1}≡1\ (mod\ p)\ p為質數$) --- :::info :::spoiler <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long #define mod 1000000007 #define maxn 100005 int factorial_inv[maxn]; int factorial[maxn]; int q_pow(int a, int x){ int sum = 1; while(x > 0){ if(x & 1)sum = (sum %mod)* (a % mod); a = (a % mod) * (a % mod); x >>= 1; } return sum % mod; } void build(){//a^(p-2)≡a^(-1) (mod p) factorial[0] = 1; for(int i = 1; i < maxn; i++){ factorial[i] = (factorial[i - 1] * i)%mod; factorial_inv[i] = q_pow(factorial[i], mod-2); } } signed main(){ ios::sync_with_stdio(0);cin.tie(0); build(); int t; cin >> t; while(t--){ string s; cin >> s; vector<int> c(1000); for(auto& i : c)i = 0; for(int i = 0; i < s.size(); i++){ c[(int)s[i]]++; } vector<int> vc; for(int i = '0'; i <= '9'; i++ ){ if(c[i] > 1)vc.push_back(c[i]); } for(int i = 'A'; i <= 'z'; i++){ if(c[i] > 1)vc.push_back(c[i]); } int ans = factorial[s.size()] % mod; for(int i = 0; i < vc.size(); i++){ ans = ans *(factorial_inv[vc[i]] % mod); ans %= mod; } cout << ans << '\n'; } } ``` ::: ![](https://i.imgur.com/y4S1rAR.png) --- ### TIOJ 2053 筆記 [題目](https://tioj.ck.tp.edu.tw/problems/2053) 這題的遞迴式是$X_n = bX_{n-1} + aX_{n-2}$ 已知 $$ \left[ \begin{array}{1} X_n \\ X_{n-1} \end{array} \right]= \left[ \begin{array}{1} b & a \\ 1 & 0 \end{array} \right] \left[ \begin{array}{1} X_{n-1} \\ X_{n-2} \end{array} \right] $$ 可知 $$ \left[ \begin{array}{1} X_{n+1} \\ X_{n} \end{array} \right]= \left[ \begin{array}{1} b & a \\ 1 & 0 \end{array} \right]^{n} \left[ \begin{array}{1} X_1 \\ X_0 \end{array} \right] $$ 得出了這個式子 接下來就只要用矩陣快速冪答案就出來了 --- :::info :::spoiler <font color="00FF00">**AC**</font>_code TIOJ 2053 ```cpp= #include<bits/stdc++.h> using namespace std; #define mod 1000000007 #define int long long struct martix{ int m[2][2]; martix operator*(martix a){ martix r; for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ r.m[i][j] = 0; } } for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ for(int k = 0; k < 2; k++){ r.m[i][j] = (r.m[i][j] + (((m[i][k] % mod) * (a.m[k][j] % mod)) % mod)) % mod; } } } return r; } }; martix qpow(martix a, int b){ martix r; for(int i = 0; i < 2; i++){ for(int j = 0; j < 2; j++){ if(i == j)r.m[i][j] = 1; else r.m[i][j] = 0; } } int n = b; for(;n;n=n >> 1){ if(n & 1)r = r * a; a = a * a; } return r; } signed main(){ int x1, x2, a, b, n; cin >> x1 >> x2 >> a >> b >> n; x1 %= mod; x2 %= mod; martix r; r.m[0][0] = b; r.m[0][1] = a; r.m[1][0] = 1; r.m[1][1] = 0; martix t = qpow(r,n - 2); int ans = (((t.m[0][0] * x2) % mod) + ((t.m[0][1] * x1) % mod)) % mod; cout << ans << '\n'; } ``` ::: ![](https://i.imgur.com/ueiNArM.png) --- ### UVA 10870 [題目](https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=489&page=show_problem&problem=1811) $f(n) = a_1 f(n - 1) + a_2 f(n - 2) + a_3 f(n - 3) + ... + a_d f(n - d) , (n > d)$ 已知前d項求第n項 這題可以將f(n)的式子列成矩陣,並用矩陣快速冪的方式快速取值 $$ \begin{bmatrix} f(n) \\ f(n- 1) \\ f(n -2) \\ \vdots \\ f(n-d+1) \end{bmatrix}= \begin{bmatrix} a_1 & a_2 & a_3 & \cdots & a_{d-1} & a_d\\ 1 & 0 & 0 & \cdots & 0 & 0 \\ 0 & 1 & 0 & \cdots & 0 & 0\\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & 1 & 0 \\ 0 & 0 & 0 & \cdots & 0 & 0 \end{bmatrix} \begin{bmatrix} f(n-1) \\ f(n- 2) \\ f(n -3) \\ \vdots \\ f(n-d) \end{bmatrix} $$ --- :::info :::spoiler <font color="00FF00">**AC**</font>_code UVA 10870 ```cpp= #include<bits/stdc++.h> #define int long long using namespace std; int d, n, mod; struct martix{ int m[16][16]; martix(){ memset(m,0,sizeof(m)); } martix operator *(martix a){ martix r; for(int i = 0; i < d; i++){ for(int j = 0; j < d; j++){ for(int k = 0; k < d; k++){ r.m[i][j] = (r.m[i][j] + ((m[i][k] % mod) * (a.m[k][j] % mod)) % mod) % mod; } } } return r; } }; martix qpow(martix a, int b){ martix r; for(int i = 0; i < d; i++){ r.m[i][i] = 1; } for(;b; b /= 2){ if(b & 1)r = r * a; a = a * a; } return r; } martix a_i, f; signed main(){ while(cin >> d >> n >> mod){ if(d==0 and n == 0 and mod == 0)break; for(int i = 0; i < d; i++){ cin >> a_i.m[0][i]; a_i.m[0][i] %= mod; } for(int i = 1; i < d; i++){ a_i.m[i][i-1] = 1; } for(int i = d - 1; i >= 0; i--){ cin >> f.m[i][0]; f.m[i][0] %= mod; } martix ans = qpow(a_i,n - d) * f; cout << ans.m[0][0] << '\n'; } } ``` ::: ![](https://i.imgur.com/5owJY3M.png) --- # 第五周 ## 預計進度 模運算講義(包含模逆元) 快速冪講義(包含矩陣快速冪) ## 講義 [模運算](/564I9dxSTf6crJP95qRnlQ) [模反元素/模逆元](/zlqbqmRxTiqprniwOmkH6Q) [快速冪&矩陣乘法&矩陣快速冪](/f6LDVf4yTNy-b4vf7kbx9g) # 第六周 ## 本周預計進度 最長遞增子序列LIS DDJ a627 DDJ a628 TIOJ 1907 ### DDJ a627 [題目](http://203.64.191.163/ShowProblem?problemid=a628) 這題就是LIS裸題 這題是a628的側資寬鬆版 可以用DP解 定義a a陣列式題目給的數列 狀態 $dp[i] =$以$a[i]$為結尾的最長遞增子序列的長度 轉移式 $dp[i] = max(所有尾巴小於a[i]的dp)$ 時間複雜度$O(N^2)$ --- :::info :::spoiler DDJ a627 <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> using namespace std; int main(){ios::sync_with_stdio(0);cin.tie(0); int t; cin >> t; while(t--){ int n; cin >> n; vector<int> a(n); for(int i = 0; i < n; i++){ cin >> a[i]; } vector<int> dp(n); fill(dp.begin(),dp.end(),1); for(int i = 0; i < n; i++){ for(int j = 0; j < i; j++){ if(a[j] < a[i]){ dp[i] = max(dp[j] + 1, dp[i]); } } } int ans = 0; for(int i = 0; i < n; i++){ ans = max(ans,dp[i]); } cout << ans << '\n'; } } ``` ::: ![](https://i.imgur.com/KyPfjnR.png) ### DDJ a628 [題目](http://203.64.191.163/ShowProblem?problemid=a628) 這題就LIS裸題 就用一個一個輸入 如果輸入大於現在LIS的尾巴 那就把輸入接在LIS後 如果輸入小於現在LIS的尾巴 那就把輸入取代LIS中第一個大於輸入的數字 這樣雖然在陣列中的LIS不一定是正確的 但長度是正確的 $O(nlogn)$ --- :::info :::spoiler DDJ a628 <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> using namespace std; int main(){ios::sync_with_stdio(0);cin.tie(0); int t; cin >> t; while(t--){ int n; cin >> n; vector<int> vc(1); cin >> vc[0]; for(int i = 1; i < n; i++){ int x; cin >> x; if(vc.back() < x){ vc.push_back(x); } else{ auto it = lower_bound(vc.begin(),vc.end(),x); *it = x; } } cout << vc.size() << '\n'; } } ``` ::: ![](https://i.imgur.com/uEhWMGJ.png) ### TIOJ 1907 這題就是剛剛LIS的基礎上加上一開始要sort 和比大小要寬度長度一起比 時間複雜度$O(nlogn)$ --- :::info :::spoiler TIOJ 1907 <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> using namespace std; bool cmp(pair<int,int> a, pair<int,int> b){ if(a.first != b.first){ return a.first < b.first; } else{ return a.second > b.second; } } int main(){ int t; cin >> t; while(t--){ int m; cin >> m; vector<pair<int, int> > vc(m); for(int i =0; i < m; i++){ cin >> vc[i].first; cin >> vc[i].second; } sort(vc.begin(),vc.end(),cmp); vector<int> x; x.push_back(vc[0].second); for(int i = 1; i < m; i++){ if(vc[i].second > x.back()){ x.push_back(vc[i].second); } else{ auto it = lower_bound(x.begin(),x.end(),vc[i].second); *it = vc[i].second; } } cout << x.size() << '\n'; } } ``` ::: ![](https://i.imgur.com/aZwcUts.png) --- # 第七周週 ## 本週預計進度 最長共同子序列LCS DDJ 310 ### DDJ 310 這題就是單純的LCS 狀態 $f(i,j)$表示$s1[1:i]$和$s2[1:j]$的$LCS$長度 初始轉移 $f(i,0)=f(0,i)=0,i>0$ 轉移式 $f(i,j)= \begin{cases} dp[i-1][j-1]+1 & a[i]=b[j]\\ max(dp[i-1][j],f(i)[j-1]) & \text{else} \end{cases}$ 要壓空間所以要滾動 時間複雜度$O(n^2)$ --- :::info :::spoiler DDJ310 <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> #define int long long using namespace std; int dp1[100005]; int dp2[100005]; signed main(){ string s,ss; while(cin >> s >> ss){ memset(dp1,0,sizeof(dp1));memset(dp2,0,sizeof(dp2)); for(int i = 1; i <= s.size(); i++){ for(int j = 1; j <= ss.size(); j++){ if(s[i-1] == ss[j-1]){ dp2[j] = dp1[j-1] + 1; } else{ dp2[j] = max(dp2[j-1] , dp1[j]); } } for(int k = 0; k <= ss.size(); k++){ dp1[k] = dp2[k]; } } cout << dp1[ss.size()] <<'\n'; } } ``` ::: ![](https://i.imgur.com/KJ20Zm3.png) --- # 第八週 ## 本週預計進度 製作學習筆記LIS LCS ### 講義 [LIS&LCS](/96ZnugpZRxqkW7LARlrWhg) # 第九週&第十週 ## 本週預計進度 DP背包問題(01背包、有限背包、無限背包) DDJ 279 330 331 ### DDJ 279 [題目](http://203.64.191.163/ShowProblem?problemid=a279) 這題就是01背包 DP存,該重量最大的價值 一個一個物品輸入(設體積V,價值K) 每次輸入都從背包拿出V的體積,並放進當前物體,並判斷原本價值大還是換成當前物體價值大並更新DP 壓成一維時為了保證遍歷$i$可以取到$i-1$的容量對應價值,所以反著遍歷,且為了避免$j - Volume$變負數 轉移式:$dp[j] = max(dp[j], d[j - Volume] + Value)$ --- :::info :::spoiler <font color="00FF00">**AC**</font>_code ddj279 ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long int n, c; int dp[1000005]; signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin >> n >> c; for(int i = 1; i <= n; i++){ int a, b; cin >> a >> b; for(int j = c; j >= b; j--){ dp[j] = max(dp[j], dp[j - b] + a); } } cout << dp[c] << '\n'; } ``` ::: ![](https://i.imgur.com/cE8S1pr.png) --- ### DDJ 330 有限背包和01背包很像,只是多遍歷一層物品數量 並為了優化複雜度將物品用2的次方分堆 假設現在有7個東西可以放 就分成1 2 4 8個就1 2 4 1 這樣原本遍歷物品個數的複雜度就會降到log --- :::info :::spoiler <font color="00FF00">**AC**</font>_code ddj330 ```cpp= #include<bits/stdc++.h> using namespace std; #define N 1000000 int dp[N+1]; int n,c; int main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); cin >> n >> c; for(int i = 0; i < n;i++){ int val, v, num; cin >> val >> v >> num; int minn = min(num,c / v); for(int k = 1; minn > 0; k *= 2){ if(k > minn)k = minn; minn -= k; for(int j = c; j >= v * k;j--){ if(j - v * k < 0)continue; dp[j] = max(dp[j],dp[j - v * k] + val * k); } } } cout << dp[c] << '\n'; } ``` ::: ![](https://i.imgur.com/bOwYb3p.png) --- ### DDJ 331 無限背包的概念也差不多 就是一個一個放放到不能放了 轉移式也差不多 --- :::info :::spoiler <font color="00FF00">**AC**</font>_code ddj331 ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long int n, c; int dp[1000005]; signed main(){ cin >> n >> c; for(int i = 0; i < n; i++){ int val, v; cin >> val >> v; for(int j = v; j <= c; j++){ dp[j] = max(dp[j], dp[j - v] + val); } } cout << dp[c] << '\n'; } ``` ::: ![](https://i.imgur.com/pQ1HW3V.png) --- # 第十一週&第十二週 ## 本週預計進度 製作學習筆記DP背包問題 [背包問題](/HWN_qcwZTum7G-qiWgV7DA) # 第十三週 ## 本週預計進度 DP優化(矩陣快速冪) neoj No.776 ## neoj No.776 [題目](https://neoj.sprout.tw/problem/776/) 我們定義$dp[x][i][j]$為$i$花了$x$步走到$j$的方法數 我們可以發現把所有差一步就到$j$的的點的方法數加起來就是到$j$的方法數 所以轉移式$dp[x][i][j] = sigma(dp[x - 1][j][k])k$為所有可以一步走到$j$的點 根據這個轉移式,我們可以再發現把第$x - 1$步的方法數乘上建圖用的鄰接矩陣就可以變$x$步的方法數 定義$DP[x]$是$dp[x]$構成的矩陣 $DP[x] = DP[x - 1][adjacency matrix]$ $DP[0]$大家都只能走到自己,所以$DP[0]就是單位矩陣$ 所以這題的解法就是把鄰接矩陣拿去做矩陣快速密 :::info :::spoiler <font color="00FF00">**AC**</font>_code ```cpp= #include<bits/stdc++.h> #define int long long #define mod 1000000007 using namespace std; int n, m, l; struct matrix { int m[101][101]; matrix(){memset(m,0,sizeof(m));} matrix operator*(matrix a){ matrix r; for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ for(int k = 1; k <= n; k++){ r.m[i][j] = (r.m[i][j] + ((m[i][k] % mod) * (a.m[k][j] % mod)) % mod) % mod; } } } return r; } }; matrix qpow(matrix a, int b){ matrix r; for(int i = 1; i <= n; i++){ r.m[i][i] = 1; } for(;b;b >>= 1){ if(b & 1)r = r * a;; a = a * a; } return r; } signed main(){ cin >> n >> m >> l; matrix g; for(int i = 0; i < m; i++){ int x, y; cin >> x >> y; g.m[x][y] = 1; } matrix ans = qpow(g, l); for(int i = 1; i <= n; i++){ for(int j = 1; j <= n; j++){ cout << ans.m[i][j] % mod; if(j < n)cout << ' '; else cout << '\n'; } } } ``` ::: ![](https://i.imgur.com/HBzrO2Y.png) # 第十四週 ## 本週預計進度 狀態壓縮 TIOJ 1452(新增) neoj No.187(暫時不作) ## TIOJ 1452 [題目](https://tioj.ck.tp.edu.tw/problems/1452) 這題是一題DP+狀態壓縮 每一行的擺放情形用一個整數儲存,該整數的每個bit代表有沒有放置東西 $ex:3 \implies 011$第一和第二有放第三沒放 定義$dp[i][j]$代表第$i$行時,有多少多少種方法可以將該行擺放成$j$二進位表示的情形 我們枚舉每行的所有擺放情形的方法數 轉移過程利用DFS轉移 如果第j個是0,就代表現在要計算的排放方法這格不用放,所以直接向(j+1)遞迴 也就是下面函式中的第二個if 如果第j個是1,就代表要放,分別DFS兩種放法,$1$是放$[i-1][j]、[i][j]$,$2$是放$[i][j]、[i][j+1]$ 也就是下面函是中的第三和第四個if 假設現在要計算110011,那第一個1就有可能和他上面的或是和右邊的組合 如果是要用第一種放法,要把上面的變0(對now操作) 假設現在要計算110011,上一層目前是111111,上一層就要變011111 --- :::info :::spoiler <font color="00FF00">**AC**</font>_code TIOJ 1452 ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long int dp[15][1 << 15]; int n, m; int dfs(int i, int j, int state, int now){ if(j == m){ return dp[i-1][now]; } if(!(state & (1 << j))){ return dfs(i, j+1, state, now); } int ret = 0; if(state & (1 << j)){//這個判斷可以不要寫,上次面的判斷已經篩過相反的情況了 ret += dfs(i, j+1, state, now - (1 << j));//也可以寫成now^(1 << x),因為now的這格bit一定是1 } if(state & (1 << (j+1))){ ret += dfs(i, j+2, state, now); } return ret; } signed main(){ios::sync_with_stdio(0);cout.tie(0); while(cin >> n >> m){ if(n == 0 && m == 0)break; memset(dp,0,sizeof(dp)); dp[0][(1 << m)-1] = 1; for(int i = 1; i <= n; i++){ for(int s = 0; s < (1 << m); s++){ dp[i][s] = dfs(i, 0, s, (1 << m)-1); } } cout << dp[n][(1 << m) - 1] << '\n'; } } ``` ::: ![](https://i.imgur.com/xgnIj7V.png) --- # 第十五週&第十六週 ## 本週預計進度 DP優化(單調對列) zerojudge a146(純單調對列) neoj No.188 No.159(159暫緩) ### zerojudge a146 筆記 [題目](https://zerojudge.tw/ShowProblem?problemid=a146) 這題就是單純的單調列沒有DP 也就是用deque儲存這個框框內有可能有利用價值的數(也就是把數列的位置記下來) 當deque裡的值超出範圍就pop掉 有更大的值出現就把小的pop掉 :::info :::spoiler zerojudge a146 <font color="00FF00">**AC**</font>code ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long int n, k; int a[1000005]; signed main(){ios::sync_with_stdio(0);cout.tie(0); while(cin >> n >> k){ if(k > n)k = n; memset(a,0,sizeof(a)); for(int i = 0; i < n; i++){ cin >> a[i]; } deque<int> qmax;//pos deque<int> qmin;//pos qmax.push_back(0); qmin.push_back(0); for(int i = 1; i < n; i++){ while(!qmin.empty() && qmin.front() <= i - k){qmin.pop_front();} while(!qmin.empty() && a[i] <= a[qmin.back()]){qmin.pop_back();} qmin.push_back(i); if(i >= k - 1){ cout << a[qmin.front()]; if(i < n - 1)cout << ' '; } }cout << '\n'; for(int i = 1; i < n; i++){ while(!qmax.empty() && qmax.front() <= i - k){qmax.pop_front();} while(!qmax.empty() && a[i] >= a[qmax.back()]){qmax.pop_back();} qmax.push_back(i); if(i >= k - 1){ cout << a[qmax.front()]; if(i < n - 1)cout << ' '; } }cout << '\n'; } return 0; } ``` ::: ![](https://i.imgur.com/mNPMbB5.png) ### neoj 188筆記 先不思考如何在這題運用單調對列 先列出一個正確的轉移式 這題40%答案的轉移式很簡單 就在最大容許距離裡找最大價值(要記得減交通成本) 轉移式: $dp[i] = val[i] + max(dp[i - k] ~ dp[i]) - 間隔數量 * c$ 所以就是兩個for迴圈 第一層跑dp 第二層跑區間最大值 :::info :::spoiler 40%code(未進行單調對列優化) ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long int t, n, k, c; int val[100005]; int dp[100005]; signed main(){ cin >> t; while(t--){ memset(dp,0,sizeof(dp)); memset(val,0,sizeof(val)); cin >> n >> k >> c; for(int i = 0; i < n ;i++){ cin >> val[i]; } dp[0] = val[0]; for(int i = 1; i < n; i++){ int maxn = 0; for(int j = 1; j <= k && i - j >= 0; j++){ maxn = max(maxn, dp[i - j] - j*c); } dp[i] = val[i] + maxn; } int ans = 0; for(int i = 0; i < n; i++){ ans = max(ans, dp[i]); } cout << ans << '\n'; } } ``` ::: #### 單調對列優化思路 在40%的code中我們可以發現我們用到的區間最大值 講到區間最大值很容易就想到線段樹 用線段樹優化的確可以,但有更快的方法 就是用上面用到的單調對列儲存區間最大值 :::info :::spoiler neoj NO188 <font color="00FF00">**AC**</font>code ```cpp= #include<bits/stdc++.h> using namespace std; #define int long long signed main(){ int t; cin >> t; while(t--){ int n, k, c; cin >> n >> k >> c; int val[n]; int dp[n]; for(auto &i : val)cin >> i; deque<int> q; dp[0] = val[0]; if(val[0] < 0)dp[0] = 0; for(int i = 0; i < n - 1; i++){ while(!q.empty() && q.front() <= i - k){q.pop_front();} while(!q.empty() && dp[q.back()] + q.back() * c <= dp[i] + i * c){q.pop_back();} q.push_back(i); if(dp[q.front()] - (i + 1 - q.front()) * c > 0){ dp[i + 1] = val[i + 1] + dp[q.front()] - (i + 1 - q.front()) * c; } else{ dp[i + 1] = val[i + 1]; } } int ans = 0; for(int i = 0; i < n; i++){ ans = max(ans, dp[i]); } cout << ans << '\n'; } return 0; } ``` ::: ![](https://i.imgur.com/MzUWZAP.png) ### neoj 159 筆記 # 第十七週&第十八週 ## 本週預計進度 製作DP優化學習筆記(包括資料結構優化) [DP優化](/dvHGezXpRu-SLAj7McPc4A)

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully