diff --git a/content/contest/template.cpp b/content/contest/template.cpp index 6690652..8720925 100644 --- a/content/contest/template.cpp +++ b/content/contest/template.cpp @@ -11,8 +11,9 @@ void solve() { signed main() { cin.tie(nullptr)->sync_with_stdio(false); int t = 1; - // cin >> t; - while (t--) solve(); + cin >> t; + while (t--) + solve(); return 0; } diff --git a/content/data-structures/DSU.h b/content/data-structures/DSU.h index a8f2904..0a86375 100644 --- a/content/data-structures/DSU.h +++ b/content/data-structures/DSU.h @@ -1,5 +1,8 @@ /** - * Author: Ramez + * Author: Lukas Polacek + * Date: 2009-10-26 + * License: CC0 + * Source: folklore * Description: Disjoint-set data structure. * Time: $O(\alpha(N))$ */ diff --git a/content/data-structures/Fenwick2dXor.h b/content/data-structures/Fenwick2dXor.h index 85e34c6..4c28684 100644 --- a/content/data-structures/Fenwick2dXor.h +++ b/content/data-structures/Fenwick2dXor.h @@ -4,54 +4,49 @@ * Time: $O(\log N \cdot \log M)$. */ #pragma once -struct Fenwick2DAdd { + +struct Fenwick2DXOR { int n, m; - vector T1, T2, T3, T4; + vector bit[2][2]; - Fenwick2DAdd(int _n, int _m) - : n(_n), m(_m), - T1(n+1, vi(m+1)), - T2(n+1, vi(m+1)), - T3(n+1, vi(m+1)), - T4(n+1, vi(m+1)) - {} + Fenwick2DXOR(int _n, int _m) : n(_n), m(_m) { + for (int px = 0; px < 2; ++px) + for (int py = 0; py < 2; ++py) + bit[px][py].assign(n+2, vi(m+2, 0)); + } - void add(vector& t, int x, int y, int v) { + void pointXOR(int x, int y, int v) { for (int i = x; i <= n; i += i & -i) for (int j = y; j <= m; j += j & -j) - t[i][j] += v; + bit[x&1][y&1][i][j] ^= v; } - void rangeAdd(int x, int y, int v) { - add(T1, x, y, v); - add(T2, x, y, v * (y - 1)); - add(T3, x, y, v * (x - 1)); - add(T4, x, y, v * (x - 1) * (y - 1)); + void rangeXOR(int x1, int y1, int x2, int y2, int v) { + if (x1 > x2 || y1 > y2) return; + auto upd = [&](int x, int y){ if (x > 0 && y > 0) pointXOR(x, y, v); }; + upd(x1, y1); + upd(x2 + 1, y1); + upd(x1, y2 + 1); + upd(x2 + 1, y2 + 1); } - void rangeUpdate(int x1, int y1, int x2, int y2, int val) { - rangeAdd(x1, y1, val); - rangeAdd(x1, y2 + 1, -val); - rangeAdd(x2 + 1, y1, -val); - rangeAdd(x2 + 1, y2 + 1, val); - } - - int prefixSum(int x, int y) const { - int s1 = 0, s2 = 0, s3 = 0, s4 = 0; + int prefixXOR(int x, int y) { + if (x <= 0 || y <= 0) return 0; + int res = 0; + int px = x & 1, py = y & 1; for (int i = x; i > 0; i -= i & -i) - for (int j = y; j > 0; j -= j & -j) { - s1 += T1[i][j]; - s2 += T2[i][j]; - s3 += T3[i][j]; - s4 += T4[i][j]; - } - return s1 * x * y - s2 * x - s3 * y + s4; + for (int j = y; j > 0; j -= j & -j) + res ^= bit[px][py][i][j]; + return res; } - int rangeQuery(int x1, int y1, int x2, int y2) const { - return prefixSum(x2, y2) - - prefixSum(x1 - 1, y2) - - prefixSum(x2, y1 - 1) - + prefixSum(x1 - 1, y1 - 1); + int rangeQuery(int x1, int y1, int x2, int y2) { + int res = 0; + res ^= prefixXOR(x2, y2); + res ^= prefixXOR(x1-1, y2); + res ^= prefixXOR(x2, y1-1); + res ^= prefixXOR(x1-1, y1-1); + return res; } -}; \ No newline at end of file +}; + diff --git a/content/data-structures/MOs.cpp b/content/data-structures/MOs.cpp new file mode 100644 index 0000000..8d2c468 --- /dev/null +++ b/content/data-structures/MOs.cpp @@ -0,0 +1,71 @@ +/** + * Author: Mohamed El-Sayed + * Description: Mo's algorithm (offline) for answering range‐distinct queries. + Sorts queries into sqrt-blocks by L, then R, and moves two pointers + to maintain current range, updating a frequency table. + * Time: O((N + Q)*sqrt(N)) Memory O(N + Q). + * Status: stress-tested + */ + +struct Query { + int l, r, idx; + bool operator<(const Query &other) const { + const int BLOCK = 450; // sqrt(max(N)) + int b1 = l / BLOCK; + int b2 = other.l / BLOCK; + if (b1 != b2) return b1 < b2; // different block by L + return r < other.r; // same block, sort by R + } +}; + +void solve() { + fastio(); + int n, q; + cin >> n >> q; + vector a(n); + for (int &x : a) cin >> x; + + // Read and store offline queries + vector queries(q); + for (int i = 0; i < q; ++i) { + int l, r; + cin >> l >> r; + queries[i] = {l - 1, r - 1, i}; // convert to 0-based + } + + // Sort queries by Mo's ordering + sort(queries.begin(), queries.end()); + + // Frequency table, current distinct count, current range [curL..curR] + const int MAXV = 1'000'005; + vector freq(MAXV, 0); + int distinct = 0, curL = 0, curR = -1; + vector ans(q); + + // Process each query by expanding/shrinking [curL..curR] + for (auto &qr : queries) { + // Expand right end + while (curR < qr.r) { + if (++freq[a[++curR]] == 1) ++distinct; + } + // Shrink right end + while (curR > qr.r) { + if (--freq[a[curR--]] == 0) --distinct; + } + // Shrink left end + while (curL < qr.l) { + if (--freq[a[curL++]] == 0) --distinct; + } + // Expand left end + while (curL > qr.l) { + if (++freq[a[--curL]] == 1) ++distinct; + } + // Record answer for this query + ans[qr.idx] = distinct; + } + + // Output all answers in original order + for (int x : ans) { + cout << x << '\n'; + } +} \ No newline at end of file diff --git a/content/data-structures/SQRTD.cpp b/content/data-structures/SQRTD.cpp new file mode 100644 index 0000000..bce0652 --- /dev/null +++ b/content/data-structures/SQRTD.cpp @@ -0,0 +1,85 @@ + +/** + * Author: Mohamed El-Sayed + * Description: Square‐root decomposition for range sum queries with point updates. + Preprocesses the array into blocks of size ~sqrt(n), maintaining the sum of each block. + query(l, r): returns the sum of arr[l..r] in O(sqrt(n)) time. + update(idx, val): updates arr[idx] to val and adjusts the corresponding block sum in O(1). + When to use: + - You have an array of size n (up to $10^5$) with mixed range‐sum queries and point updates. + - You need better performance than O(n) per operation but segment trees are overkill. + * Time: O(sqrt(n)) per query, O(1) per update. + * Status: stress-tested + */ +#include +using namespace std; + +using vi = vector; + +int n, q, SQ; +vi arr, blkSum; + +// Build block sums from the initial array in O(n) +void preProcess() { + // For each element, add it to its block's sum + for (int i = 0; i < n; ++i) { + int blk = i / SQ; + blkSum[blk] += arr[i]; + } +} + +// Query the sum in the interval [l, r] in O(sqrt(n)) +int query(int l, int r) { + int ans = 0; + // Process elements until we reach block boundary or exceed r + while (l <= r) { + // If l is at the start of a block and the whole block lies within [l, r] + if (l % SQ == 0 && l + SQ - 1 <= r) { + ans += blkSum[l / SQ]; + l += SQ; + } else { + // Otherwise, take the single element + ans += arr[l]; + ++l; + } + } + return ans; +} + +// Point update: set arr[idx] = val in O(1) +void update(int idx, int val) { + int blk = idx / SQ; + // Remove old value from block sum and add new value + blkSum[blk] -= arr[idx]; + arr[idx] = val; + blkSum[blk] += val; +} + +void solve() { + // Read array size and number of operations + cin >> n >> q; + arr.resize(n); + cin >> arr; + + // Determine block size and initialize block sums + SQ = ceil(sqrt(n)); + blkSum.assign((n + SQ - 1) / SQ, 0); + + preProcess(); + + while (q--) { + int op; + cin >> op; + if (op == 1) { + // Update operation: 1 pos val + int pos, val; + cin >> pos >> val; + update(pos - 1, val); + } else { + // Query operation: 2 l r + int l, r; + cin >> l >> r; + cout << query(l - 1, r - 1) << "\n"; + } + } +} \ No newline at end of file diff --git a/content/data-structures/PrefixSum2D.h b/content/data-structures/SubMatrix.h similarity index 64% rename from content/data-structures/PrefixSum2D.h rename to content/data-structures/SubMatrix.h index 8fa845a..c9c0b8b 100644 --- a/content/data-structures/PrefixSum2D.h +++ b/content/data-structures/SubMatrix.h @@ -13,13 +13,13 @@ #pragma once template -struct PrefixSum2D { +struct SubMatrix { vector> p; - PrefixSum2D(vector>& v) { - int R = v.size(), C = v[0].size(); - p.assign(R + 1, vector(C + 1)); - for (int r = 0; r < R; r++) for (int c = 0; c < C; c++) - p[r + 1][c + 1] = v[r][c] + p[r][c + 1] + p[r + 1][c] - p[r][c]; + SubMatrix(vector>& v) { + int R = sz(v), C = sz(v[0]); + p.assign(R+1, vector(C+1)); + rep(r,0,R) rep(c,0,C) + p[r+1][c+1] = v[r][c] + p[r][c+1] + p[r+1][c] - p[r][c]; } T sum(int u, int l, int d, int r) { return p[d][r] - p[d][l] - p[u][r] + p[u][l]; diff --git a/content/data-structures/Trie.cpp b/content/data-structures/Trie.cpp deleted file mode 100644 index b91031c..0000000 --- a/content/data-structures/Trie.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Author: Ramez - * Description: Trie - * Time: $O(1)$ time, $O(N)$ space - */ - -struct Trie { - struct Node { - Node* child[26]; - int IsEnd, Prefix; - Node() { - memset(child, 0, sizeof child); - IsEnd = Prefix = 0; - } - }; - - Node* root = new Node(); - - void insert(string& s) - { - Node* cur = root; - for (auto it : s) - { - int idx = it - 'a'; - if (cur->child[idx] == 0) - { - cur->child[idx] = new Node(); - } - cur = cur->child[idx]; - cur->Prefix++; - } - cur->IsEnd++; - } - - bool searchWord(string& s) - { - Node* cur = root; - for (auto it : s) - { - int idx = it - 'a'; - if (cur->child[idx] == 0)return 0; - cur = cur->child[idx]; - } - return cur->IsEnd; - } - - int countWord(string& s) - { - Node* cur = root; - for (auto it : s) - { - int idx = it - 'a'; - if (cur->child[idx] == 0)return 0; - cur = cur->child[idx]; - } - return cur->IsEnd; - } - - int countPrefix(string& s) - { - Node* cur = root; - for (auto it : s) - { - int idx = it - 'a'; - if (cur->child[idx] == 0)return 0; - cur = cur->child[idx]; - } - return cur->Prefix; - } -}; \ No newline at end of file diff --git a/content/data-structures/chapter.tex b/content/data-structures/chapter.tex index 11711d4..cc4e47c 100644 --- a/content/data-structures/chapter.tex +++ b/content/data-structures/chapter.tex @@ -12,14 +12,16 @@ \chapter{Data structures} \kactlimport{HashMap.h} \kactlimport{DSU.h} \kactlimport{DSURollback.h} -\kactlimport{PrefixSum2D.h} +\kactlimport{SubMatrix.h} \kactlimport{Matrix.h} \kactlimport{LineContainer.h} \kactlimport{Treap.h} \kactlimport{RMQ.h} \kactlimport{MoQueries.h} \kactlimport{MergeSortTree.h} -\kactlimport{Trie.cpp} +% \kactlimport{Trie.cpp} \kactlimport{BinaryTrie.cpp} +\kactlimport{SQRTD.cpp} +\kactlimport{MOs.cpp} diff --git a/content/number-theory/Eratosthenes.h b/content/number-theory/Eratosthenes.h new file mode 100644 index 0000000..33c4857 --- /dev/null +++ b/content/number-theory/Eratosthenes.h @@ -0,0 +1,25 @@ +/** + * Author: Håkan Terelius + * Date: 2009-08-26 + * License: CC0 + * Source: http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes + * Description: Prime sieve for generating all primes up to a certain limit. isprime$[i]$ is true iff $i$ is a prime. + * Time: lim=$10^8$ $\approx$ 0.8 s. Runs 30\% faster if only odd indices are stored. + * Status: Tested + */ +#pragma once + +const int MAX_PR = 5'000'000; +bitset isprime; +vi sieve(int lim) { + isprime.set(); isprime[0] = isprime[1] = 0; + for (int i = 4; i < lim; i += 2) isprime[i] = 0; + for (int i = 3; i*i < lim; i += 2) if (isprime[i]) + for (int j = i*i; j < lim; j += i*2) isprime[j] = 0; + vi pr; + + for (int i = 2; i < lim; i++) + if (isprime[i]) pr.push_back(i); + + return pr; +} diff --git a/content/number-theory/FastSieve.h b/content/number-theory/FastSieve.h index 4fac3f0..e901cc1 100644 --- a/content/number-theory/FastSieve.h +++ b/content/number-theory/FastSieve.h @@ -37,4 +37,3 @@ vi eratosthenes() { for (int i : pr) isPrime[i] = 1; return pr; } - diff --git a/content/number-theory/Ncr.h b/content/number-theory/Ncr.h index 92c0f84..055543f 100644 --- a/content/number-theory/Ncr.h +++ b/content/number-theory/Ncr.h @@ -1,6 +1,7 @@ /** * Author: ezz - * Description: Precomputes factorials and inverse factorials modulo mod, call build\_fact once before using nCr. + * Description: Precomputes factorials and inverse factorials modulo mod, call + * build\_fact once before using nCr. */ #include "ModPow.h" @@ -9,19 +10,20 @@ vector fact = {1}, inv = {1}; void build_fact(int n = 2e6) { - fact.resize(n + 1); inv.resize(n + 1); - rep(i, 1, n + 1) - fact.push_back(1LL * fact.back() * i % mod); + for (int i = 1; i <= n; i++) + fact.push_back(fact.back() * i % mod); inv[n] = modpow(fact[n], mod - 2); for (int i = n - 1; i >= 0; --i) - inv[i] = 1LL * inv[i + 1] * (i + 1) % mod; + inv[i] = inv[i + 1] * (i + 1) % mod; } int ncr(int n, int r) { if (r < 0 || r > n) return 0; return fact[n] * inv[r] % mod * inv[n - r] % mod; // For npr: return fact[n] * inv[n - r] % mod; + // ncr(n + r - 1, n) for stars and bars } + diff --git a/content/strings/Trie.h b/content/strings/Trie.h new file mode 100644 index 0000000..ca3bf64 --- /dev/null +++ b/content/strings/Trie.h @@ -0,0 +1,49 @@ +/** + * Author: ezz + * Description: trie that supports update(val, op), + * where op = +1 to insert, op = –1 to erase + * Time: $O(1)$ time, $O(N)$ space + */ + +struct Trie { + struct Node { + int ch[26]{}; + int cnt = 0, end = 0; + }; + vector t = {{}}; + + void update(string s, int op) { + int u = 0; + for (char c : s) { + int v = c - 'a'; + if (!t[u].ch[v]) { + if (op == -1) return; + t[u].ch[v] = t.size(); + t.push_back({}); + } + u = t[u].ch[v]; + t[u].cnt += op; + } + t[u].end += op; + } + + bool find(string s) { + int u = 0; + for (char c : s) { + int v = c - 'a'; + if (!t[u].ch[v]) return false; + u = t[u].ch[v]; + } + return t[u].end > 0; + } + + int prefix(string s) { + int u = 0; + for (char c : s) { + int v = c - 'a'; + if (!t[u].ch[v]) return 0; + u = t[u].ch[v]; + } + return t[u].cnt; + } +}; diff --git a/content/strings/chapter.tex b/content/strings/chapter.tex index 7db9b77..fea0be9 100644 --- a/content/strings/chapter.tex +++ b/content/strings/chapter.tex @@ -7,4 +7,5 @@ \chapter{Strings} \kactlimport{SuffixArray.h} \kactlimport{SuffixTree.h} \kactlimport{Hashing.h} +\kactlimport{Trie.h} \kactlimport{AhoCorasick.h}