# TFCIS2024暑訓正式賽題解 ## 廢話 **這些題目大多是我突然想到的東東,所以有些地方會有破綻之類的XD** **但基本上都是些演算法。** **然後這次是打算把題目名稱取難一點,也算是一種梗了(?** **假如tobiichi3227突然開發出了hack的話,我大概會一個一個看你們的code順便增強我的測資吧XD** ## 預期難度 **pA < pB < pC < pD < pE < pF** ## 題解 :::success **可以先看 hints 想一想 再看解答** ::: ### pA 破台 首殺:ezy_plate :::spoiler **hint 1** **看清楚題目** ::: :::spoiler **hint 2** **題目說什麼就是什麼** ::: :::spoiler **pA題解** **"TFcis" 是有包含""的** **但是要在C++裡創建字串還要加反斜線\\** **諧咖題XD** **程式碼** ```cpp= #include <iostream> using namespace std; int main() { string s; getline(cin, s); if(s == "\"TFcis\"") cout << "TFcis yes yes\n"; else cout << "Ha Ha the contest is too ez\n"; } ``` ::: ### pB Treap 首殺:我 :::spoiler **hint 1** **多看題目** ::: :::spoiler **hint 2** **Treap的性質是什麼?** ::: :::spoiler **hint 3** **Treap為什麼要被發明出來** ::: :::spoiler **pB題解** **一題梗題** **就算沒學過Treap,只要仔細看一下題目就會發現 : Treap其實就是一個BST(二分樹),而上面就有說了->二分樹可能會退化成鍊狀結構** **所以Treap只是發明出來降低這個概率,但實際上最糟還是會發生** **答案就是所有節點加起來-1** **程式碼** ```cpp= #include <iostream> using namespace std; int main() { int n; cin >> n; int ans = 0; while(n--) { int k; cin >> k; ans+=k; int j; while(k--) cin >> j; } cout << ans-1 << '\n'; } ``` **BTW** ![omob](https://hackmd.io/_uploads/ryK4sKqFA.png=80%x) **這個也蠻好笑的XD** **多執行幾次範例測資就會發現mxdp會浮動** **原因是因為我pri是隨機的哈哈** ::: ### pC 最佳線段選擇 首殺:我 :::spoiler **hint 1** **先轉化題目性質** ::: :::spoiler **hint 2** **要怎麼取線段呢?** ::: :::spoiler **hint 3** **這題是Greedy!** ::: :::spoiler **pC題解** **題目寫得很長,但其實就是一題Greedy** **證明 :** **假設選擇了 $Line_i$ 再選擇了 $Line_j$** **其 $Line_i = A_i \cdot x + B_i$ $Line_j = A_j \cdot x + B_j$** **那麼 $A_j \cdot (A_i \cdot x + B_i) + B_j > A_i \cdot (A_j \cdot x + B_j) + B_i$** **稍微整理一下 :** $A_j \cdot (A_i \cdot x + B_i) + B_j > A_i \cdot (A_j \cdot x + B_j) + B_i$ $\to A_j \cdot A_i \cdot x + A_j \cdot B_i + B_j > A_i \cdot A_j \cdot x + A_i \cdot B_j + B_i$ $\to A_j \cdot B_i + B_j > A_i \cdot B_j + B_i$ **只要把 $x$ 消掉後就可以快樂sort了** **程式碼** ```cpp= #include <bits/stdc++.h> using namespace std; #define ll long long int main() { ios::sync_with_stdio(false), cin.tie(0); int n; while(cin >> n) { vector<pair<ll, ll> > l(n); for(auto &d : l) cin >> d.first; for(auto &d : l) cin >> d.second; /* Ll = al(x) + bl, Lr = ar(x) + br select l before r is best : ar(alx + bl) + br > al(arx + br) + bl => aralx + arbl + br > aralx + albr + bl => arbl + br > albr + bl */ sort(l.begin(), l.end(), [&](auto Ll, auto Lr) { auto &[al, bl] = Ll; auto &[ar, br] = Lr; return ar*bl + br > al*br + bl; }); ll x = 0; for(auto &[a, b] : l) { x = a*x + b; } cout << x << '\n'; } } ``` **BTW** **這題我修了兩次(~~官解寫錯兩次~~** ::: ### pD 喪屍圍城 首殺:charhao :::spoiler **pD題解** **[搬運題 - abc363_e](https://atcoder.jp/contests/abc363/tasks/abc363_e)** ::: ### pE 排列組合-走格子 首殺:我 :::spoiler **hint 1** **分析一下複雜度** ::: :::spoiler **hint 2** **要讓你的C變得有效率** ::: :::spoiler **pE 題解** [**TOJ 071**](https://toj.tfcis.org/oj/pro/71/) **這題是一題噁心數學題** **是Lucas** **相信大家都沒聽過XD** **詳細作法OI WIKI上有** **39分解 :** ```cpp= #include <bits/stdc++.h> using namespace std; #define ll long long #define int long long const int mod = 27751; ll fpw(ll a, ll b) { if(b==0) return 1LL; ll re = fpw(a, b>>1); if(b&1) return re*re%mod*a%mod; return re*re%mod; } ll C(ll n, ll m) { ll re = 1; for(ll i = n; i >= n-m+1; i--) { re *= i%mod; re %= mod; } for(ll i = 1; i <= m; i++) { re *= fpw(i, mod-2); re %= mod; } return re; } signed main() { ios::sync_with_stdio(false), cin.tie(0); int n; cin >> n; ll x = 0, y = 0; ll re = 1; for(int i = 0; i < n; i++) { ll a, b; cin >> a >> b; re *= C(a-x + b-y, a-x)%mod; re %= mod; x = a, y = b; } cout << re << '\n'; } ``` **滿分解 :** ```cpp= #include <bits/stdc++.h> using namespace std; #define ll long long const int mod = 27751; ll fra[mod], infra[mod]; ll fpw(ll a, ll b) { if(b==0) return 1LL; ll re = fpw(a, b>>1); if(b&1) return re*re%mod*a%mod; return re*re%mod; } ll C(ll n, ll m) { if(n < m) return 0; return fra[n]%mod*infra[m]%mod*infra[n-m]%mod; } ll lucas(ll n, ll m) { if(m==0) return 1; return C(n%mod, m%mod)%mod * lucas(n/mod, m/mod)%mod; } int main() { ios::sync_with_stdio(false), cin.tie(0); fra[0] = 1, infra[0] = 1; for(ll i = 1; i < mod; i++) fra[i] = fra[i-1]*i%mod, infra[i] = fpw(fra[i], mod-2); int n; cin >> n; ll x = 0, y = 0; ll ans = 1; for(int i = 0; i < n; i++) { ll xx, yy; cin >> xx >> yy; ans *= lucas(xx-x+yy-y, xx-x)%mod, ans %= mod; x = xx, y = yy; } cout << ans << '\n'; } ``` ::: ### pF 樹套樹 首殺:我 :::spoiler **hint 1** **先分析複雜度** ::: :::spoiler **hint 2** **這題根本不用樹套樹** ::: :::spoiler **hint 3** **看看pB的題目名稱** ::: :::spoiler **pF 題解** **只要有學過Treap的都做得出來** **tw87說BIT上二分搜也可以** **得出結論 : 模板題** **BIT上二分搜 (by tw87 orz) :** ```cpp= #include<bits/stdc++.h> #define all(v) v.begin(), v.end() using namespace std; struct BIT{ vector< int > tree; int n; BIT(int N){ n = N, tree.resize(N + 2); } void modify(int id, int val){ for(; id <= n; id += id&-id) tree[id] += val; } int query(int id){ int re = 0; for(; id; id-=id&-id) re += tree[id]; return re; } int query(int l, int r){ return query(r) - query(l - 1); } int kth(int k){ int ans = 0; for(int i = __lg(n); i >= 0; --i){ if((ans | (1 << i)) > n) continue; if(k - tree[ans | (1 << i)] > 0){ ans |= (1 << i); k -= tree[ans]; } } return ans + 1; } }; int main(){ int n, q; cin >> n >> q; vector< array<int, 3> > ask(q); vector< int > num{-1, 8, 7}; for(auto &[t, a, b] : ask){ cin >> t; if(t == 1) cin >> a >> b, num.push_back(b); else cin >> a; } sort(all(num)); num.resize(unique(all(num)) - num.begin()); auto get = [&](int t)->int { return lower_bound(all(num), t) - num.begin(); }; BIT bit(num.size()); vector< int > v(n + 1); for(int i = 1; i <= n; ++i){ v[i] = (i & 1) ? 8 : 7; bit.modify(get(v[i]), 1); } for(auto &[t, a, b] : ask){ if(t == 1){ bit.modify(get(v[a]), -1); bit.modify(get(b), 1); v[a] = b; } else cout << num[bit.kth(a)] << "\n"; } } ``` **Treap (by blameazu) :** ```cpp= #include <bits/stdc++.h> using namespace std; mt19937 rng(time(NULL)); struct Treap { int pri, sz, val; Treap *l, *r; Treap(int x) : val(x), pri(rng()), sz(1) { l = r = nullptr; } void pull() { sz = 1; if(l) sz += l->sz; if(r) sz += r->sz; } static int getsz(Treap* v) { return v ? v->sz : 0; } static Treap* merge(Treap *a, Treap *b) { if(!a || !b) return a ? a : b; if(a->pri > b->pri) return a->r = merge(a->r, b), a->pull(), a; else return b->l = merge(a, b->l), b->pull(), b; } static pair<Treap*, Treap*> splitsz(Treap *v, int k) { if(!v) return {nullptr, nullptr}; v->pull(); if(getsz(v->l) < k) { auto p = splitsz(v->r, k - getsz(v->l) - 1); return v->r = p.first, v->pull(), make_pair(v, p.second); } else { auto p = splitsz(v->l, k); return v->l = p.second, v->pull(), make_pair(p.first, v); } } static pair<Treap*, Treap*> splitval(Treap *v, int k) { if(!v) return {nullptr, nullptr}; v->pull(); if(v->val <= k) { auto p = splitval(v->r, k); return v->r = p.first, v->pull(), make_pair(v, p.second); } else { auto p = splitval(v->l, k); return v->l = p.second, v->pull(), make_pair(p.first, v); } } static void insert(Treap*& root, Treap* node) { auto p = splitval(root, node->val); root = merge(merge(p.first, node), p.second); root->pull(); return; } static void erase(Treap*& root, int k) { auto p = splitval(root, k-1); auto q = splitsz(p.second, 1); delete q.first; root = merge(p.first, q.second); root->pull(); return; } static int kth(Treap*& v, int k) { auto p = splitsz(v, k-1); auto q = splitsz(p.second, 1); if (!q.first) return -1; int ans = q.first->val; v = merge(p.first, merge(q.first, q.second)); return ans; } }; int main() { ios::sync_with_stdio(false), cin.tie(0); int n; cin >> n; vector<int> v(n+1, 0); Treap* root = nullptr; for(int i = 1; i <= n; i++) {v[i] = (i&1 ? 8 : 7); Treap::insert(root, new Treap(v[i]));} int q; cin >> q; while(q--) { int c; cin >> c; if(c == 1) { int x, y; cin >> x >> y; Treap::erase(root, v[x]); Treap::insert(root, new Treap(y)); v[x] = y; } else { int k; cin >> k; cout << Treap::kth(root, k) << '\n'; } } } ``` :::