--- tags: 109_FDCS --- # FDforces Round #1 (Div. 1) Tutorial ## p1: a196. 競賽用hello world (600) > [name=by fdhs107_KonChin_Shih][color=#1f1e33] ### code ```python= print("hello, world") ``` --- ## p2: a466. 國土大夢想家 (1200) > [name=by fdhs107_KonChin_Shih][color=#1f1e33] ### 想法 看一堆人用`if`判所有么九牌,我整個panik 顯然用STL就好了,整個code不長 ### code ```cpp= #include<iostream> #include<set> #define endl '\n' using namespace std; inline void solve() { string str; set<string> s{"1m", "9m", "1p", "9p", "1s", "9s", "1z", "2z", "3z", "4z", "5z", "6z", "7z"}; for (int n = 13; n--;) cin >> str, s.erase(str); cout << (s.empty() ? 13 : s.size() > 1 ? 0 : 1) << endl; } main() { cin.tie(nullptr); ios::sync_with_stdio(false); int t; cin >> t; while (t--) solve(); return 0; } ``` :::spoiler code by fdhs109_GT ```cpp= #include <bits/stdc++.h> using namespace std; inline void solve() { set<string> ss{"1m", "9m", "1p", "9p", "1s", "9s", "1z", "2z", "3z", "4z", "5z", "6z", "7z"}; set<string> s; string tmp; int res = 0; for (int i = 0; i < 13; i++) cin >> tmp, s.insert(tmp); for (const auto& i : ss) res += s.count(i); cout << (res == 13 ? res : res == 12 ? 1 : 0) << '\n'; } int main() { ios::sync_with_stdio(false), cin.tie(nullptr); int t; cin >> t; while (t--) solve(); return 0; } ``` ::: --- ## p3: a457. 就說期中考會考了,還敢不寫作業啊 (1500) > [name=by fdhs109_GT][color=#c665f7] ### 想法 看到 `code` 自己想一想, 這是作業題, 也都說可以貼模板 (?) 了, 應該過吧 :poop: 反正就是 `.` 先判斷, 然後再用 `string` 的 `operator>` 來判斷就好了 ><, 看到好多人寫 $60$ 幾行, 還有人寫 $100$ 多行, 我就好爽 :poop: 。 ### code ```cpp= // from giver #include <bits/stdc++.h> using namespace std; int main() { ios::sync_with_stdio(false), cin.tie(nullptr); string s1, s2; while (cin >> s1 >> s2) { string ts1 = s1, ts2 = s2; if (s1.find(".") == -1) s1 += "."; if (s2.find(".") == -1) s2 += "."; int p1 = s1.find("."); int p2 = s2.find("."); if (p1 != p2) cout << (p1 > p2 ? ts2 : ts1) << "\n"; else cout << min(ts1, ts2) << "\n"; } } ``` :::spoiler Python AC code by fdhs107_KonChin_Shih ```python= from decimal import * from sys import stdin getcontext().prec = 1000005 for a in stdin: a = Decimal(a) b = Decimal(stdin.readline()) print(min(a, b)) ``` ::: --- ## p4: a433. KonChin走格子 (1600) > [name=by fdhs108_38002][color=#FFD700] ~~此題的定位是水題~~ 有寫作業的人,這題至少要拿$50\%$ ### Subtask 1(20%) $x, y\leq10, q \leq \frac{xy}{2}$ 先說個題外話 高中的程式競賽(TOI, 學科...)或是檢定(APCS),基本上都是採IOI賽制,所以一題會有多個測資點讓你去解,基本上越後面的測資點越難拿,而前面的測資點大部分只要模擬、暴力解,都可以喇到。 爆搜其實算是比較基礎的解題想法(?),比起其他演算法設計想法(DP, Greedy...),其實比較容易做出來 那這邊就不贅述爆搜解了,相信大家都能想出來 XD ### Subtask 2(30%) $x, y\leq10^2, q \leq \frac{xy}{2}$ $O(xyq)$ 勉強能喇過,因為我詢問的點沒卡每次都是最右下角的點 有寫作業[a422: GT走格子](http://203.64.191.163/ShowProblem?problemid=a422)的人,至少該拿到這筆測資 #### 想法 可以用比較數學的想法去思考這題,對於一點(x, y)來說他被走到的次數會是$${x+y\choose x}{(q_x - x) + (q_y - y)\choose q_x - x}$$ 所以對於每個詢問的答案會是$$\sum\limits_{i = 1}^{q_x}\sum\limits_{j = 1}^{q_y}{i+j\choose i}{(q_x - i) + (q_y - j)\choose q_x - i} \times weight(i, j)$$ ::: spoiler code by revival0728 (33419 傅興) ```cpp= #include<bits/stdc++.h> #define endl '\n' using namespace std; const int mxX = 2e3, mxY = mxY, M = 1000000007; int x, y, q; int dp[mxX][mxX], val[mxX][mxX]; long long ans; int main() { cin.tie(0), ios_base::sync_with_stdio(0); for(int i = 0; i < mxX; ++i){ dp[0][i] = 1; } for(int i = 0; i < mxX; ++i){ dp[i][0] = 1; } for(int i = 1; i < mxX; ++i){ for(int j = 1; j < mxX; ++j){ dp[i][j] = dp[i-1][j] + dp[i][j-1], dp[i][j] %= M; } } while(cin >> x >> y >> q){ for(int i = 0; i < y; ++i){ for(int j = 0; j < x; ++j){ cin >> val[i][j]; } } int a, b; while(q--){ ans = 0; cin >> a >> b; for(int i = 0; i < b; ++i){ for(int j = 0; j < a; ++j){ ans += (1LL * (dp[i][j] % M) * dp[b-i-1][a-j-1] % M) * val[i][j], ans %= M; } } cout << ans << endl; } } } ``` ::: ### Subtask 3(50%) $x, y\leq 2\times10^3, q \leq \frac{xy}{2}$ 再說一個題外話 只要題目不是那種超毒瘤的題目,通常都會先從測資範圍判斷該如何下手 例如這題$x, y\leq 2\times10^3$ input的複雜度是$O(xy)$, output是$O(1)$ 也就是說這題的複雜度絕對不會比$O(xy)$還好,畢竟輸入就要$O(xy)$ 再來詢問次數是$\frac{xy}{2}$,所以對於每次詢問要是$O(1)$,才可能使總複雜度為$O(xy)$ 接下來就需要一點的經驗,來想出這題是dp 我們可以先假設$dp[i][j]$是以$(i, j)$為終點的所有路徑價值和 而對於點$(i, j)$而言,只可能從$(i - 1, j)$或$(i, j - 1)$過來 也就是說$dp[i][j]$,是$dp[i - 1][j] + dp[i][j - 1] +$從$(i - 1, j)或(i, j - 1)$過來的可能總數乘上該點的價值。 dp轉移式 : $$dp[i][j] = dp[i - 1][j] + dp[i][j - 1] + {i + j\choose i} \times weight(i, j)$$ 所以每次詢問就輸出$dp[q_x][q_y]$就好了,$O(1)$ 總複雜度 $O(xy + q)$ 又要再說一個題外話 如果你確定你code的複雜度是好的,但卻TLE,很有可能是常數太大 XD 雖然一般的題目都不會卡常數,但如果不小心被卡到的話,就盡量把不需要的操作省略,或是做其他優化(bitset, I/O, ...) ::: spoiler code by fdhs108_38002 ```cpp= const int N = 3000 + 5; int cnt[N][N], dp[N][N]; inline void solve() { int n, m, q; cin >> m >> n >> q; ms(cnt, 0), ms(dp, 0); cnt[1][1] = 1; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) if(i != 1 || j != 1) cnt[i][j] = 1ll * (cnt[i - 1][j] + cnt[i][j - 1]) % MOD; for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) { ll tmp; cin >> tmp; dp[i][j] = 1ll * (dp[i - 1][j] + dp[i][j - 1] + tmp * cnt[i][j] % MOD) % MOD; } while(q--) { int a, b; cin >> a >> b; cout << dp[b][a] << '\n'; } } ``` ::: ::: spoiler code by fdhs109_dylanoggy (✰いと爆死✰) ```cpp= #include<iostream> #include<vector> #include<map> #include<cmath> #define endl '\n' #define _ cin.tie(nullptr),ios::sync_with_stdio(false); using namespace std; using ll=long long; int main(){_ int x,y,q; cin>>x>>y>>q; vector<vector<ll>>v(y,vector<ll>(x,0)); for(int i=0;i<y;i++)for(int j=0;j<x;j++)cin>>v[i][j]; int ansx,ansy; vector<vector<ll>>m(y,vector<ll>(x,0)); vector<vector<ll>>ans(y,vector<ll>(x,0)); m[0][0]=1; ans[0][0]=v[0][0]; for(int j=1;j<x;j++){ m[0][j]=1; ans[0][j]=(v[0][j]+ans[0][j-1])%1000000007; } for(int i=1;i<y;i++){ ans[i][0]=(ans[i-1][0]+v[i][0])%1000000007; m[i][0]=1; for(int j=1;j<x;j++){ m[i][j]=(m[i][j-1]+m[i-1][j])%1000000007; ans[i][j]=(ans[i-1][j]+ans[i][j-1])%1000000007; ans[i][j]=(ans[i][j]+m[i][j]*v[i][j]%1000000007)%1000000007; } } for(int i=0;i<q;i++){ cin>>ansx>>ansy; cout<<ans[ansy-1][ansx-1]<<endl; } } ``` ::: ::: spoiler code by Fdhs109_qazwsx (33325劉承豐) ```cpp= #include<bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; ll p[2005][2005], r[2005][2005]; int main() { memset(p, 0, sizeof(p)), memset(r, 0, sizeof(p)); cin.tie(0); ios_base::sync_with_stdio(false); int x,y,q; cin>>x>>y>>q; r[0][1]=1; for(int i=1;i<y+1;i++) { for(int j=1;j<x+1;j++) { cin>>p[i][j]; p[i][j]=p[i][j]%mod; r[i][j]=(r[i-1][j]+r[i][j-1])%mod; p[i][j]=(p[i-1][j]+p[i][j-1]+r[i][j]*p[i][j])%mod; } } for(int a=0;a<q;a++) { int qx,qy; cin>>qx>>qy; cout<<p[qy][qx]<<'\n'; } } ``` ::: :::spoiler code by fdhs107_KonChin_Shih ```cpp= #include<iostream> #include<vector> #include<algorithm> #define endl '\n' using namespace std; constexpr int mod = 1e9 + 7; int main() { cin.tie(nullptr); ios::sync_with_stdio(false); int n, m, q, k, a, b; cin >> m >> n >> q; vector<vector<int>> arr(n, vector<int>(m, 1)); for (int i = 1; i < n; i++) for (int j = 1; j < m; j++) arr[i][j] = (arr[i - 1][j] + arr[i][j - 1]) % mod; vector<vector<int>> v(n + 1, vector<int>(m + 1, 0)); for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) cin >> k, v[i + 1][j + 1] = (1LL * arr[i][j] * k + v[i][j + 1] + v[i + 1][j]) % mod; while (q--) cin >> b >> a, cout << v[a][b] << endl; return 0; } ``` ::: --- ## p5: a465. Lelwen走迷宮 (1600) > [name=by fdhs108_38002][color=#FFD700] ~~此題的定位是水題~~ 在考試的時候,發現上課有上到類似題[b554: 5.貪吃龍遊戲](https://hackmd.io/@konchin/Brute-force#b554-5%E8%B2%AA%E5%90%83%E9%BE%8D%E9%81%8A%E6%88%B2) 所以更應該要寫出這題(? 跟上題補充的一樣,從測資範圍可看出爆搜沒問題 所以問題在於寫不寫得出來,那這就是看你練習量的多寡了 考試時測資太水被隨便唬爛過,已更新 XD ::: spoiler code by fdhs108_38002 ```cpp= const int N = 8; int n, w[N][N], ans = 1e9; bool v[N][N]; // 是否可走 const pii go[4] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; //pair<int, int> #define nx (x + go[i].F) // go[i].first #define ny (y + go[i].S) // go[i].second void dfs(int x, int y, int cnt = w[0][0]) { if(cnt > ans) return; // 剪枝 if(x == n - 1 && y == n - 1) { ans = cnt; return; } for(int i = 0; i < 4; ++i) if(nx >= 0 && nx < n && ny >= 0 && ny < n && !v[nx][ny]) { v[x][y] = 1; dfs(nx, ny, cnt + w[nx][ny]); v[x][y] = 0; } } inline void solve() { cin >> n; ms(v, 0), ms(w, 0); //memset(v, 0, sizeof(v)) for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) { char c; cin >> c; v[i][j] = (c == 'X'); } for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) cin >> w[i][j]; dfs(0, 0); cout << (ans == 1e9 ? -1 : ans) << '\n'; } ``` ::: :::spoiler code by fdhs107_KonChin_Shih ```cpp= #include<iostream> #include<vector> #include<algorithm> #include<functional> #include<tuple> #define endl '\n' using namespace std; int main() { cin.tie(nullptr); ios::sync_with_stdio(false); int n, res; cin >> n; string str; vector<vector<int>> v(n, vector<int>(n)); for (int i = 0; i != n; i++) cin >> str; for (auto& i : v) for (auto& j : i) cin >> j; for (auto& i : v) replace(i.begin(), i.end(), 0, int(1e9)); auto check = [&](int x, int y) { return x >= 0 && y >= 0 && x < n && y < n && v[x][y] != 1e9; }; const pair<int, int> arr[] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}}; function<int(int, int)> dfs = [&](int x, int y) { if (x == n - 1 && y == n - 1) return v[x][y]; int minn = 1e9, tmp = v[x][y]; v[x][y] = 1e9; for (const auto& i : arr) { int a, b; tie(a, b) = i; if (check(x + a, y + b)) minn = min(minn, dfs(x + a, y + b)); } v[x][y] = tmp; return min(int(1e9), minn + v[x][y]); }; res = dfs(0, 0); cout << (res == 1e9 ? -1 : res) << endl; return 0; } ``` ::: :::spoiler code by fdhs109_GT ```cpp= #include <bits/stdc++.h> using namespace std; vector<vector<int>> mp; vector<vector<bool>> vst; int res = 1e9, n, ans = 0; string s; void dfs(int y, int x, int sum = 0) { sum += mp[y][x]; if (y == n && x == n) { res = min(res, sum), ans = 1; return; } if (x > 1 && mp[y][x - 1] && !vst[y][x - 1]) // left vst[y][x] = 1, dfs(y, x - 1, sum), vst[y][x] = 0; if (y > 1 && mp[y - 1][x] && !vst[y - 1][x]) // up vst[y][x] = 1, dfs(y - 1, x, sum), vst[y][x] = 0; if (x < n && mp[y][x + 1] && !vst[y][x + 1]) // right vst[y][x] = 1, dfs(y, x + 1, sum), vst[y][x] = 0; if (y < n && mp[y + 1][x] && !vst[y + 1][x]) // down vst[y][x] = 1, dfs(y + 1, x, sum), vst[y][x] = 0; } #define _ ios::sync_with_stdio(false), cin.tie(nullptr); int main() { _ cin >> n; for (int i = 0; i < n; cin >> s, i++); mp.resize(n + 1, vector<int>(n + 1)); vst.resize(n + 1, vector<bool>(n + 1)); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; cin >> mp[i][j++]); dfs(1, 1), cout << (ans ? res : -1) << '\n'; } ``` ::: --- ## p6: a458. 都說這題是水題了,還不趕快來解這題啊 (1700) > [name=by fdhs109_GT][color=#c665f7] ### 想法 - 水題 ✅ 阿我都把解法打在題敘中了, 應該要 `AC` ✅。 $p_{l}\times p_{(l+1)}\times p_{(l+2)}\times\dots\times p_r\\=質數乘積表[r]\div 質數乘積表[l-1]$ 所以, 只要建好質數乘積表, 對於每次查詢就可以快速得到 $[L,\ R]$ 的質數乘積, 但是模運算不適用於乘法, 所以要用模逆元 `inv(x) = fastpower(x, mod - 2, mod)`, 所求$\implies 質數乘積表[r]\times inv(質數乘積表[l-1])\mod 998244353$, ### 模逆元 結論簡單, 用背的就可以, 但身為教學組, 還是來寫一下 $證明(?)$ 好了 :poop: by `Fermat's little theorem` : $a^p-a$ 是 $p$ 的倍數 (for p is a prime number), $\implies a^p\equiv a\mod p$ $\implies a^{(p-1)}\equiv1\mod p$, where $a \ne pk, k\in N$ $\implies a\times a^{(p-2)}\equiv1\mod p$, where $, where $a \ne pk, k\in N$ 概念同於 $a\times\frac{1}{a}\equiv1(\mod p)$ $\implies a^{(p-2)}\mod p$ 就是 $a$ 的乘法反元素, ### code ```cpp= #include <bits/stdc++.h> using namespace std; constexpr int mod = 998244353; vector<int> v{2}, fac(1000001, 1); // v 質數表, fac 質數乘積表 int fpow(int a, int b, int m) { // fast power 快速冪 int ret = 1; for (; b; b >>= 1, a = 1LL * a * a % m) if (b & 1) ret = 1LL * ret * a % m; return ret; } int inv(int x) { // inverse, 乘法反元素 return fpow(x, mod - 2, mod); } int main() { ios::sync_with_stdio(false), cin.tie(nullptr); // 質數表, 順便建質數乘積表 for (int i = 2; i <= 1000000; i++) { bool f = 1; for (const auto& j : v) { if (j * j > i) break; // 只要判斷到 sqrt 就好了 if (i % j == 0) { f = 0; break; } } if (f) v.push_back(i), fac[i] = 1LL * fac[i - 1] * i % mod; else fac[i] = fac[i - 1]; } for (int l, r; cin >> l >> r;) { // 所求 = fac[r] / fac[l - r] // 但因為模運算不適用於除法 // 所以要用乘法反元素 fac[r] * inv(fac[l - 1]) cout << 1LL * fac[r] * inv(fac[l - 1]) % mod << '\n'; } } ``` :::spoiler code by fdhs107_KonChin_Shih ```cpp= #include<iostream> #include<vector> #include<algorithm> #include<cmath> #define endl '\n' using namespace std; constexpr int mod = 998244353; long long fpow(long long a, long long b) { long long ret = 1; for (a %= mod; b; b >>= 1, a = a * a % mod) if (b & 1) ret = ret * a % mod; return ret; } inline long long rev(long long a) {return fpow(a, mod - 2);} int main() { cin.tie(nullptr); ios::sync_with_stdio(false); vector<bool> table(1000001, true); for (int i = 2, end = sqrt(1000000); i <= end; i++) if (table[i]) for (int j = i << 1; j <= 1000000; j += i) table[j] = false; vector<int> prime, prefix{1}; for (int i = 2; i <= 1000000; i++) if (table[i]) prime.emplace_back(i); for (auto& i : prime) prefix.emplace_back(1LL * i * prefix.back() % mod); int l, r; while (cin >> l >> r) { int a = lower_bound(prime.begin(), prime.end(), l) - prime.begin(); int b = upper_bound(prime.begin(), prime.end(), r) - prime.begin(); cout << prefix[b] * rev(prefix[a]) % mod << endl; } return 0; } ``` ::: ::: spoiler code by fdhs108_38002 其實可以多建一個模逆元的表,使複雜度從 $O(n\ln\ln n + q\log k)$ 變成 $O(n\ln\ln n + q)$ 其中$O(n\ln\ln n)$是Eratosthenes質數篩法的複雜度,q是詢問次數 ```cpp= inline ll power(ll a, ll b) { ll ret = 1; for(; b; a = 1ll * a * a % mod, b >>= 1) if(b & 1) ret = 1ll * ret * a % mod; return ret; } const ll N = 1e6; vector<ll> fact, infact; vector<bool> flag; void build() { infact = fact = vector<ll>(N + 1); flag = vector<bool>(N + 1, 0); fact[0] = fact[1] = 1; for(ll i = 2; i <= N; i++) if(!flag[i]) { for(ll j = i * i; j <= N; j += i) flag[j] = 1; } for(int i = 2; i <= N; ++i) { if(!flag[i]) fact[i] = fact[i - 1] * i % mod; else fact[i] = fact[i - 1]; } infact[N] = power(fact[N], mod - 2); for(int i = N - 1; i >= 0; --i) infact[i] = infact[i + 1] * (!flag[i + 1] ? i + 1 : 1) % mod; } inline void solve() { build(); int l, r; while(cin >> l >> r) { cout << fact[r] * infact[l - 1] % mod << '\n'; } } ``` ::: --- ## p7: a464. 你懂海嗎 (1800) > [name=by fdhs107_KonChin_Shih][color=#1f1e33] ### 想法 這題是DP,主要的目標就是維護會出現的所有可能 最直覺的想法就是拿map存價格跟海產的編號 然後每個月複製那個map之後把價格加上$d_i$再放回去 並判斷`m.count(k)`就能找出所有的可能 不過map在存取插入都有一個log存在 時間應該會不夠(?)(有沒有人想試可以試試看 所以需要更快的做法 可以開一個整數dp陣列存1~10000目前可以從哪個海產經過波動後達到 然後每個月就只要用以下式子轉移(邊界自己判) 如果$dp[j]$存在海產的編號則$dp_[j+d_i]=dp_i[j]$ ### code ```cpp= #include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { vector<int> v(10000, 0); int n, m, k, d, t = 0, tmp; cin >> n >> m >> k; for (int i = 1; i <= n; i++) cin >> tmp, v[tmp - 1] = i; if (v[k - 1]) goto result; for (t = 1; t <= m; t++) { cin >> d; if (d > 0) for (int i = 9999; i >= 0; i--) v[min(9999, i + d)] = v[i] ? : v[min(9999, i + d)]; if (d < 0) for (int i = 0; i <= 9999; i++) v[max(0, i + d)] = v[i] ? : v[max(0, i + d)]; if (v[k - 1]) goto result; } cout << -1 << endl; return 0; result: cout << t << ' ' << v[k - 1] << endl; return 0; } ```