<center> <h1> week of 12/12 </h1> here we go... </center> --- ### `📝 notes` USACO returns this friday. --- ## contest reflection ([cf round #837](https://codeforces.cc/contest/1771)) ### performance: horrible, i solved the first two problems, which were incredibly standard in the first 10 minutes. but died on implementation for p2 for the next hour. if i was more focused on debugging problem 2, i could have probably solved problem 3. nonetheless, it was enough to promote to the Pupil rank in CodeForces. Which, for just participating in only $5$ contests, is just alright. ### problems: #### hossam and combinatorics (https://codeforces.cc/contest/1771/problem/A) simply find occurences of the min & max, and do $2 \cdot (\text{min} \cdot \text{max})$, if all elements are the same, do $n \cdot (n - 1)$. remember to use long longs to avoid overflow. ```cpp= #include "bits/stdc++.h" using namespace std; using ll = long long; #define all(x) begin(x), end(x) #define rall(x) x.rbegin(), x.rend() #define sz(x) (int)(x.size()) #define inf 1000000010 #define linf 0x3f3f3f3f3f3f3f3f #define endl "\n" #define f first #define s second #define pi pair<int, int> #ifdef LOCAL #include "/mnt/c/yukon/dbg.cpp" #else #define dbg(...) 0 #endif #define int ll template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) { for (int i = 0; i < sz(v); ++i) { os << v[i] << ' '; } return os; } void solve() { int n; cin >> n; vector<int> a(n); map<int, int> occ; for (int i = 0; i < n; i++) { cin >> a[i]; occ[a[i]]++; } bool same = 1; for (int i = 1; i < n; i++) { if (a[i] != a[i - 1]) same = 0; } if (same) { cout << (n * (n - 1)) << endl; return; } sort(all(a)); cout << (occ[a[0]] * occ[a.back()]) * 2 << endl; } signed main() { cin.tie(0)->sync_with_stdio(0); int tc; cin >> tc; for (int _ = 0; _ < tc; _++) { solve(); #ifdef LOCAL cout << "____________________" << endl; #endif } } // don't get stuck on one approach // question bounds // flesh out your approach before implementing o.o ``` #### hossam and friends (https://codeforces.cc/contest/1771/problem/B) we'll count subsegments, by the number of subsegments starting at $i$ for all $i$. the longest we can go is, the min of all friends from $i \dots n$. we can precalculate a suffix array for this. ```cpp= #include "bits/stdc++.h" using namespace std; using ll = long long; #define all(x) begin(x), end(x) #define rall(x) x.rbegin(), x.rend() #define sz(x) (int)(x.size()) #define inf 1000000010 #define linf 0x3f3f3f3f3f3f3f3f #define endl "\n" #define f first #define s second #define pi pair<int, int> #ifdef LOCAL #include "/mnt/c/yukon/dbg.cpp" #else #define dbg(...) 0 #endif #define int ll template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) { for (int i = 0; i < sz(v); ++i) { os << v[i] << ' '; } return os; } void solve() { int n, m; cin >> n >> m; vector<int> mini(n + 1, inf); for (int i = 0; i < m; i++) { int u, v; cin >> u >> v; if (u > v) swap(u, v); mini[u] = min(mini[u], v); } vector<int> back(n + 2, inf); for (int i = n; ~i; --i) { back[i] = min(back[i + 1], mini[i]); } int ans = 0; for (int i = 1; i < n; i++) { int len = back[i] - i - 1; if (back[i] == inf) { len = (n + 1) - i - 1; } // cout << i << ' ' << len << endl; // cout << i << ' ' << mini[i] << ' ' << len << endl; ans += len; } cout << ans + n << endl; } signed main() { cin.tie(0)->sync_with_stdio(0); int tc; cin >> tc; for (int _ = 0; _ < tc; _++) { solve(); #ifdef LOCAL cout << "____________________" << endl; #endif } } // don't get stuck on one approach // question bounds // flesh out your approach before implementing o.o ``` #### hossam and trainees (https://codeforces.cc/contest/1771/problem/C) we want to find if any two elements in the array have a $\text{gcd} > 1$. the elements go up to $10^9$, and a property about the prime factorizations for a large number like $10^9$ is that the degree of a prime $> \sqrt{10^9}$ will only have a degree of one. so, we'll just calcualate all primes up until $\sqrt{10^9}$, and check for prime factor occurences. ```cpp= #include "bits/stdc++.h" using namespace std; using ll = long long; #define all(x) begin(x), end(x) #define rall(x) x.rbegin(), x.rend() #define sz(x) (int)(x.size()) #define inf 1000000010 #define linf 0x3f3f3f3f3f3f3f3f #define endl "\n" #define f first #define s second #define pi pair<int, int> #ifdef LOCAL #include "/mnt/c/yukon/dbg.cpp" #else #define dbg(...) 0 #endif #define int ll template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) { for (int i = 0; i < sz(v); ++i) { os << v[i] << ' '; } return os; } vector<bool> pp(32000 + 1); vector<int> lst; void solve() { int n; cin >> n; vector<int> a(n); map<int, int> occ; for (int i = 0; i < n; i++) { cin >> a[i]; for (int j : lst) { if (a[i] % j == 0) { if (occ[j] > 0) { dbg(occ); cout << "YES" << endl; for (int k = i + 1; k < n; k++) cin >> a[k]; return; } occ[j]++; } while (a[i] % j == 0) { a[i] /= j; } } if (a[i] > 1) { if (occ[a[i]] > 0) { dbg(occ); cout << "YES" << endl; for (int j = i + 1; j < n; j++) cin >> a[j]; return; } occ[a[i]]++; } } cout << "NO" << endl; } signed main() { cin.tie(0)->sync_with_stdio(0); pp[0] = pp[1] = true; for (int i = 2; i * i <= (int)(32000); i++) { if (!pp[i]) { for (int j = i * i; j <= (int)(32000); j += i) pp[j] = true; } } for (int i = 2; i <= (int)(32000); i++) { if (!pp[i]) lst.push_back(i); } int tc; cin >> tc; for (int _ = 0; _ < tc; _++) { solve(); #ifdef LOCAL cout << "____________________" << endl; #endif } } // don't get stuck on one approach // question bounds // flesh out your approach before implementing o.o ``` #### [hossam and (sub-)palindromic tree](https://codeforces.cc/contest/1771/problem/D) Consider storing the answer for some interval in $dp_{l,r}$. Where $l$ and $r$ represent the left and right endpoints in the interval, respectively. Then, our transitions would be: $$ dp_{l,r} = \left\{\begin{array}{lr} dp(l + 1, r - 1) + 2, & s_l = s_r\\ \max(dp_{l+1,r}, dp_{l, r-1}), & s_l \neq s_r\\ \end{array}\right\} $$ That's pretty simple, especially since we're allotted $\mathcal{O}(n^2)$, the only caveat being that we have to find these intervals on a tree. In order to process this, we'll compute distances between every pair of nodes. Then, between two nodes $l, r$. $l + 1$ will simply be the node that's adjacent to $l$ which is closer to $r$ than $l$. The only special case is if $l = r$, then $dp_{l,r} = 1$. time complexity: $\mathcal{O}(n^2)$. ```cpp= #include "bits/stdc++.h" using namespace std; using ll = long long; #define all(x) begin(x), end(x) #define rall(x) x.rbegin(), x.rend() #define sz(x) (int)(x.size()) #define inf 1000000010 #define linf 0x3f3f3f3f3f3f3f3f #define endl "\n" #define f first #define s second #define pi pair<int, int> #ifdef LOCAL #include "/mnt/c/yukon/dbg.cpp" #else #define dbg(...) 0 #endif template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) { for (int i = 0; i < sz(v); ++i) { os << v[i] << ' '; } return os; } void solve() { int n; cin >> n; string s; cin >> s; vector<vector<int>> adj(n); for (int i = 0; i < n - 1; i++) { int u, v; cin >> u >> v; --u; --v; adj[u].push_back(v); adj[v].push_back(u); } // first compute distances o(n^2) vector<vector<int>> dist(n, vector<int>(n)); for (int i = 0; i < n; i++) { function<void(int u, int par)> dfs = [&](int u, int par) { for (int v : adj[u]) { if (v == par) continue; dist[i][v] = (par == -1 ? 0 : dist[i][u]) + 1; dfs(v, u); } }; dfs(i, -1); dbg(i, dist[i]); } // bfs vector<vector<int>> dp(n, vector<int>(n, -1)); queue<pi> todo; for (int i = 0; i < n; i++) todo.push({i, i}); while (!todo.empty()) { pi top = todo.front(); todo.pop(); if (dp[top.f][top.s] != -1) continue; int xmm = -1, ymm = -1; // <- l, r -> for (int i : adj[top.f]) { if (dist[i][top.s] < dist[top.f][top.s]) { xmm = i; break; } } for (int i : adj[top.s]) { if (dist[i][top.f] < dist[top.f][top.s]) { ymm = i; break; } } if (top.f == top.s) { dp[top.f][top.s] = 1; dp[top.s][top.f] = dp[top.f][top.s]; } else { dp[top.f][top.s] = max({dp[top.f][top.s], dp[xmm][top.s], dp[top.f][ymm]}); if (s[top.f] == s[top.s]) { dp[top.f][top.s] = max(dp[top.f][top.s], max(0, dp[xmm][ymm]) + 2); } dp[top.s][top.f] = dp[top.f][top.s]; } for (int i : adj[top.f]) { if (i != xmm) todo.push({i, top.s}); } for (int i : adj[top.s]) { if (i != ymm) todo.push({top.f, i}); } } int ans = 1; for (int i = 0; i < n; i++) { for (int j = i; j < n; j++) { ans = max(ans, dp[i][j]); } } cout << ans << endl; } int main() { cin.tie(0)->sync_with_stdio(0); int tc; cin >> tc; for (int _ = 0; _ < tc; _++) { solve(); #ifdef LOCAL cout << "____________________" << endl; #endif } } // don't get stuck on one approach // question bounds // flesh out your approach before implementing o.o ``` #### hossam and a letter we have a $400 \times 400$ grid, and we have to find the largest "H" that can be constructed given that it cannot contain any "#"s, and can only contain one "m". this is another common "construct the largest letter" type of problem. for this problem, we'll find the maximum height a stick can go up at $(i,j)$ for all $(i, j)$, and also the maximum height going down. we'll also add an extra dimension of size two to dictate whether we've already used our "m." then, for all $(i, j)$, we'll stem a stick starting at $(i, j)$, and the second one at $(i, j + k)$. Then, the total amount used is $2(\min(up_{i,j}, up_{i,j+k}) + \min(down_{i,j}, down_{i,j+k})) + k$. If we haven't used the "m" on the horizontal, we'll use it on either the up or the down. ```cpp= // midsummer madness, i can't take it no more #include "bits/stdc++.h" using namespace std; using ll = long long; #define all(x) begin(x), end(x) #define rall(x) x.rbegin(), x.rend() #define sz(x) (int)(x.size()) #define inf 1000000010 #define linf 0x3f3f3f3f3f3f3f3f #define endl "\n" #define f first #define s second #define pi pair<int, int> #ifdef LOCAL #include "/mnt/c/yukon/dbg.cpp" #else #define dbg(...) 0 #endif template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) { for (int i = 0; i < sz(v); ++i) { os << v[i] << ' '; } return os; } int main() { cin.tie(0)->sync_with_stdio(0); int n, m; cin >> n >> m; vector<vector<char>> v(n, vector<char>(m)); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> v[i][j]; } } vector<vector<vector<int>>> up(n, vector<vector<int>>(m, vector<int>(2))); vector<vector<vector<int>>> down(n, vector<vector<int>>(m, vector<int>(2))); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { // vertical line stemming from (i, j) // up int pp = i - 1; int md = 0; while (~pp) { if (v[pp][j] == 'm') md++; if (v[pp][j] == '#') break; if (md > 1) break; up[i][j][1] = max(up[i][j][1], i - pp); if (md == 0) { up[i][j][0] = max(up[i][j][0], i - pp); } pp--; } pp = i + 1; md = 0; while (pp < n) { if (v[pp][j] == 'm') md++; if (v[pp][j] == '#') break; if (md > 1) break; down[i][j][1] = max(down[i][j][1], pp - i); if (md == 0) { down[i][j][0] = max(down[i][j][0], pp - i); } pp++; } } } int ans = 0; for (int i = 0; i < n; i++) { for (int j = 0; j + 2 < m; j++) { // draw line starting from (i, j) if (v[i][j] == '#' || v[i][j + 1] == '#') continue; int md = (v[i][j] == 'm') + (v[i][j + 1] == 'm'); // extend rightward for (int k = j + 2; k < m; k++) { if (v[i][k] == 'm') md++; if (v[i][k] == '#') break; if (md > 1) break; // use md up, down, or center if (md == 1) { // force int mup = min(up[i][j][0], up[i][k][0]); int mdown = min(down[i][j][0], down[i][k][0]); if (mup && mdown) { ans = max( ans, 2 * (mup + mdown) + (k - j + 1)); } } else { // up int fs = max(min(up[i][j][0], up[i][k][1]), min(up[i][j][1], up[i][k][0])); int mdown = min(down[i][j][0], down[i][k][0]); if (fs && mdown) { ans = max( ans, 2 * (fs + mdown) + (k - j + 1)); } dbg(ans, i, k); // down int mup = min(up[i][j][0], up[i][k][0]); fs = max(min(down[i][j][0], down[i][k][1]), min(down[i][j][1], down[i][k][0])); if (mup && fs) { ans = max( ans, 2 * (fs + mup) + (k - j + 1)); } } } } } cout << ans << endl; } // don't get stuck on one approach // question bounds // flesh out your approach before implementing o.o ``` #### lucky chains here, we have to find the longest "lucky" chain that starts with $a, b$. a lucky chain is described a chain of $(a,b), (a+1, b+1), (a+2, b+2), \dots, (a+k, b+k)$, where all pairs $a, b$ have a GCD of $1$. Answer $n \leq 10^6$ queries. ##### `solution` wlog assume that $b > a$, remember that the idea behind Euclid's GCD Algorithm relies on the fact that $\gcd(a, b) = \gcd(a, b-a)$. thus, $\gcd(a+k,b+k) = \gcd(b - a, a + k)$. we just have to find the smallest $k$. obviously, $a + k \equiv 0 \pmod{d}$, where $d$ is a divisor of $b - a$. (actually we only need to consider prime factors). and so, we'll just find the closest multiple of $d$ greater than $a + k$, and subtract $a$. There are at most $\log_2(n)$ prime divisors of $n$. We'll calculate this with eratosthenes' sieve and slightly alter it to store the maximum prime divisor. Also if $b-a = 1$, then the answer is $-1$, since it can be extended infinitely. ```cpp= #include "bits/stdc++.h" using namespace std; using ll = long long; #define all(x) begin(x), end(x) #define rall(x) x.rbegin(), x.rend() #define sz(x) (int)(x.size()) #define inf 1000000010 #define linf 0x3f3f3f3f3f3f3f3f #define endl "\n" #define f first #define s second #define pi pair<int, int> #ifdef LOCAL #include "/mnt/c/yukon/dbg.cpp" #else #define dbg(...) 0 #endif template <typename T> ostream& operator<<(ostream& os, const vector<T>& v) { for (int i = 0; i < sz(v); ++i) { os << v[i] << ' '; } return os; } vector<int> isp(1e7 + 1, -1); void solve() { int a, b; cin >> a >> b; if (__gcd(a, b) > 1) { cout << 0 << endl; } else if (a % 2 && b % 2) { cout << 1 << endl; } else if (abs(a - b) == 1) { cout << -1 << endl; } else { int dd = abs(a - b); ll ans = inf; while (isp[dd] != -1) { ll mlt = ((a + isp[dd] - 1) / isp[dd]) * isp[dd]; ans = min(ans, mlt - a); dd /= isp[dd]; } cout << ans << endl; } } int main() { cin.tie(0)->sync_with_stdio(0); for (int i = 2; i <= (int)(1e7); i++) { if (isp[i] == -1) { for (int j = i; j <= (int)(1e7); j += i) { if (isp[j] == -1) isp[j] = i; } } } int tc; cin >> tc; for (int _ = 0; _ < tc; _++) { solve(); #ifdef LOCAL cout << "____________________" << endl; #endif } } // don't get stuck on one approach // question bounds // flesh out your approach before implementing o.o ``` alright, im gonna start the December USACO Contest now. GLHF!