diff --git a/caderno/caderno.tex b/caderno/caderno.tex index 69a50d616cbe191c20b4c08a34d7748fa7ec0f49..133c185fdb13644359c9e6af8032cccc1e026148 100644 --- a/caderno/caderno.tex +++ b/caderno/caderno.tex @@ -8,8 +8,10 @@ \usepackage{amsmath} \usepackage{stfloats} \usepackage{caption} +\usepackage{siunitx} \usepackage{multirow} \usepackage{fontspec} +\usepackage{amssymb} \usepackage{hyperref} \usepackage{xcolor} \setmonofont[ @@ -168,6 +170,108 @@ \end{align*} Para $p$ primo, $n_i$ e $m_i$ são coeficientes das representações de $n$ e $m$ na base $p$. +\subsection{Teorema do Chicken McNugget} +Dados dois números coprimos $n$ e $m$, o maior número que não pode ser escrito como +uma combinação linear deles é $nm - n - m$. +\begin{itemize} + \item Existem $\frac{(n-1)(m-1)}{2}$ inteiros não-negativos que não podem + ser escritos como uma combinação linear de $n$ e $m$. + \item Para cada par $(k, nm - n - m - k)$, para $k \ge 0$, exatamente um pode + ser escrito. +\end{itemize} + +\subsection{Triplas pitagóricas} +Para todo $a, b, c \in \mathbb{N}$ satisfazendo $a^2 + b^2 = c^2$, existem +$m, n \in \mathbb{N}$ e $m > n$ de tal forma que: +\begin{multicols}{3} + $$a = m^2 - n^2$$ \break + $$b = 2mn$$ \break + $$c = m^2 + n^2$$ +\end{multicols} + +\subsection{Razões trigonométricas} +\begin{center} +\begin{tabular}{cccc} + \hline + & $\ang{30}$ & $\ang{45}$ & $\ang{60}$ \\ + \hline + $\sin \theta$ & $\frac{1}{2}$ & $\frac{\sqrt{2}}{2}$ & $\frac{\sqrt{3}}{2}$ \\ + $\cos \theta$ & $\frac{\sqrt{3}}{2}$ & $\frac{\sqrt{2}}{2}$ & $\frac{1}{2}$ \\ + $\tan \theta$ & $\frac{\sqrt{3}}{3}$ & $1$ & $\sqrt{3}$ \\ + \hline +\end{tabular} +\end{center} + +\subsection{Soma de ângulos} +\begin{align*} + \sin(a \pm b) = \sin a \cos b \pm \cos a \sin b \\ + \cos(a \pm b) = \cos a \cos b \mp \sin a \sin b \\ + \tan(a \pm b) = \frac{\tan a \pm \tan b}{1 \mp \tan a \tan b} \\ +\end{align*} + +\subsection{Grafos planarares} +\begin{itemize} + \item Se $G$ tem $k$ componentes conectadas, então $n - m + f = k + 1$. + \item $m \le 3n - 6$. Se $G$ não tem triângulos, $m \le 2n - 4$. + \item O grau mínimo é $\le 5$. E pode ser 6-colorido em $\mathcal{O}(n+m)$. +\end{itemize} + +\subsection{Corte mínimo} +O corte mínimo pode ser encontrado em grafos não-direcionados com o algoritmo +de Stoer-Wagner. Entre dois vértices, o Teorema do Fluxo Máximo e +Corte Mínimo afirma que o fluxo máximo é igual ao peso total das arestas do +custo mínimo. Para achar o corte, faça uma busca em profundidade partindo da +fonte, percorrendo apenas as arestas com capacidade residual. Todos os +vértices alcançados fazem parte de uma das partes do corte mínimo. + +\subsection{Fatorial e desarranjos} +\begin{align*} + n! &\approxeq \sqrt{2\pi n} \left(\frac{n}{e}\right)^n \\ + !n &= n!\sum_{i=0}^{n} \frac{(-1)^i)}{i!} \text{ para } n \ge 0 \\ + !n &= \left\lfloor \frac{n! + 1}{e} \right\rfloor \text{ para } n \ge 1 \\ + !n &= n(!(n-1)) + (-1)^n \text{ para } n > 0 +\end{align*} + +\subsection{Teorema binomial} +\begin{align*} + (x + y)^n &= \sum_{k=0}^{n} \binom{n}{k} x^{n-k}y^k = \sum_{k=0}^{n} \binom{n}{k} x^ky^{n-k} \\ + (x + 1)^n &= \sum_{k=0}^{n} \binom{n}{k} x^k +\end{align*} + +\subsection{Círculo inscrito em triângulo} +O centro do círculo forma três triângulos. A área do triângulo $ABC$ é igual a +$\frac{rp}{2}$ onde $p$ é o perímetro do triângulo. + +\subsection{Lei dos senos} +Dado um triângulo inscrito em um círculo de raio $r$, vale que +\begin{align*} + \frac{a}{\sin A} = \frac{b}{\sin B} = \frac{c}{\sin C} = 2r +\end{align*} + +\subsection{Lei dos cossenos} +Triângulo $ABC$ com lados $a$, $b$ e $c$, a lei dos cossenos: +\begin{align*} +a^2 = b^2 + c^2 - 2bc \cdot \cos A +\end{align*} +E podemos derivar a inequalidade triangular: +\begin{align*} + c^2 \le a^2 + b^2 + 2ab = (a + b)^2 +\end{align*} + +\subsection{Truque da distância Manhattan} +Seja $\mathcal{L}((x, y)) = (x+y, x-y)$. Temos as seguintes funções de distância: +\begin{itemize} + \item Manhattan: $M(p, q) = |p.x - q.x| + |p.y - q.y|$. + \item Chebyshev: $C(p, q) = \max(|p.x - q.x|, |p.y - q.y|)$. +\end{itemize} +Então: +\begin{itemize} + \item $\mathcal{L}(\mathbb{Z}^2)$ escala $\mathbb{Z}^2$ em $\sqrt{2}$ e rotaciona em $\frac{\pi}{4}$ em sentido horário. + \item Para algum $p \in \mathbb{Z}^2$, $\mathcal{L}(\{ q : M(q, p) \le d \})$ (círculo) forma um quadrado alinhado + nos eixos em $\mathcal{L}(\mathbb{Z})^2$, com canto inferior-esquerda em $\mathcal{L}(p) - (d, d)$ e canto superior-direito em $\mathcal{L}(p) + (d, d)$. + \item $M(p, q) = C(\mathcal{L}(p), \mathcal{L}(q))$, e $C(p, q) = M(\mathcal{L}^{-1}(p), \mathcal{L}^{-1}(q))$. +\end{itemize} + \subsection{Números de Catalão} {\sloppy 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, @@ -335,6 +439,9 @@ para $a$ e $m$ coprimos. \subsection{Árvore de segmentos preguiçosa iter.} \lstinputlisting{fontes-com-hash/lazy-segment-tree.h} +\subsection{Nó de Kadane} +\lstinputlisting{fontes-com-hash/kadane.h} + \subsection{Hash customizado} \lstinputlisting{fontes-com-hash/custom-hash.h} @@ -351,8 +458,8 @@ para $a$ e $m$ coprimos. \subsection{Articulações e pontes} \lstinputlisting{fontes-com-hash/articulations-bridges.h} -\clearpage +\clearpage \subsection{Componentes fortes -- Tarjan} \lstinputlisting{fontes-com-hash/tarjan.h} @@ -451,6 +558,9 @@ para $a$ e $m$ coprimos. \end{tabular} \lstinputlisting{fontes-com-hash/kuhn.h} +\subsection{Emparelhamento Generalizado -- Blossom} +\lstinputlisting{fontes-com-hash/blossom.h} + \subsection{Decomposição raiz quadrada de árvore} \lstinputlisting{fontes-com-hash/tree-square-root-decomposition.h} @@ -465,6 +575,9 @@ para $a$ e $m$ coprimos. \subsection{Inteiro modular} \lstinputlisting{fontes-com-hash/modular-integer.h} +\subsection{Transformação de Fourier Modular} +\lstinputlisting{fontes-com-hash/number-theoretic-transform.h} + \subsection{Matriz} \lstinputlisting{fontes-com-hash/matrix.h} @@ -668,6 +781,9 @@ $z[i]$ é o tamanho da maior string que é ao mesmo tempo, um prefixo de $s$ e u \end{tabular} \lstinputlisting{fontes-com-hash/monotone.h} +\subsection{Medidor giratório} +\lstinputlisting{fontes-com-hash/rotating-calipers.h} + \subsection{Árvore KD} \lstinputlisting{fontes-com-hash/kd-tree.h} diff --git a/fontes/binary-pow.h b/fontes/binary-pow.h index f9e74e7143fee75941a6bc04763e2f501d2f4e30..6bfcaa366bc28e39f4d6a367ca75e78dc62216e0 100644 --- a/fontes/binary-pow.h +++ b/fontes/binary-pow.h @@ -1,8 +1,7 @@ -ll binary_pow(ll& res, ll a, ll e) { - if (e == 0) { return res = 1; } - if (e == 1) { return res = a; } - /* ll res = */ binary_pow(res, a, e/2); - res = res * res; - if (e % 2) res = res * a; +mint binary_pow(mint a, ll e) { + if (e == 0) { return 1; } + mint res = binary_pow(a, e/2); + res *= res; + if (e % 2) { res *= a; } return res; } diff --git a/fontes/blossom.h b/fontes/blossom.h new file mode 100644 index 0000000000000000000000000000000000000000..e835c48ff96dda9b6772a48a8215f77348015798 --- /dev/null +++ b/fontes/blossom.h @@ -0,0 +1,67 @@ +int n; +vector<int> match (N), par (N), base (N), vis (N); +queue<int> q; + +void contract(int u, int v, bool first = 1) { + static vector<bool> bloss; static int l; + if (first) { + bloss = vector<bool>(n, 0); + vector<bool> ok (n, 0); + int k = u; l = v; + while (1) { + ok[k = base[k]] = 1; + if (match[k] == -1) break; + k = par[match[k]]; + } + while (!ok[l = base[l]]) l = par[match[l]]; + } + while (base[u] != l) { + bloss[base[u]] = bloss[base[match[u]]] = 1; + par[u] = v; v = match[u]; u = par[match[u]]; + } + if (!first) { return; } + contract(v, u, 0); + for (int u = 0; u < n; u++) if (bloss[base[u]]) { + base[u] = l; + if (!vis[u]) { q.push(u); } + vis[u] = 1; + } +} + +int getpath(int s) { + for (int i = 0; i < n; i++) base[i] = i, par[i] = -1, vis[i] = 0; + vis[s] = 1; q = queue<int>(); q.push(s); + while (q.size()) { + int u = q.front(); q.pop(); + for (int i : g[u]) { + if (base[i] == base[u] || match[u] == i) { continue; } + if (i == s or (match[i] != -1 && par[match[i]] != -1)) + contract(u, i); + else if (par[i] == -1) { + par[i] = u; + if (match[i] == -1) { return i; } + i = match[i]; + vis[i] = 1; q.push(i); + } + } + } + return -1; +} + +int blossom() { + int ans = 0; + fill(all(match), -1); + for (int i = 0; i < n; i++) if (match[i] == -1) + for (int j : g[i]) if (match[j] == -1) { + match[i] = j; match[j] = i; ans++; break; + } + for (int i = 0; i < n; i++) if (match[i] == -1) { + int j = getpath(i); if (j == -1) { continue; } + ans++; + while (j != -1) { + int p = par[j], pp = match[p]; + match[p] = j; match[j] = p; j = pp; + } + } + return ans; +} diff --git a/fontes/kadane.h b/fontes/kadane.h new file mode 100644 index 0000000000000000000000000000000000000000..76dec7c7df4505528e417d8f92294e13b15cfc14 --- /dev/null +++ b/fontes/kadane.h @@ -0,0 +1,13 @@ +struct kadane { + int sum, pref, suff, ans; + kadane() : sum(-oo) { pref = suff = ans = 0; } + kadane(int x) : sum(x) { pref = suff = ans = max(x, 0); } + kadane operator+(kadane const& r) { + auto l = *this; kadane a; + a.sum = max(l.sum + r.sum, -oo); + a.pref = max(l.pref, l.sum + r.pref); + a.suff = max(r.suff, r.sum + l.suff); + a.ans = max({ l.ans, r.ans, l.suff + r.pref }); + return a; + } +} diff --git a/fontes/lazy-segment-tree.h b/fontes/lazy-segment-tree.h index 7969c44ecb3741babd0b7e96483896b1ab6c088f..9ad17ad67e963b5a32d27d45eab7468a27280c73 100644 --- a/fontes/lazy-segment-tree.h +++ b/fontes/lazy-segment-tree.h @@ -3,7 +3,7 @@ struct dlta { int add = 0, set = -1; }; vector<ll> t (2*N); vector<dlta> delta (2*N); void build(vector<int>& src, int n) { - for (int i = 1; i < src.size(); i++) + for (int i = 1; i <= n; i++) t[n+i] = src[i]; for (int ti = n-1; ti > 0; ti--) t[ti] = OP(t[2*ti], t[2*ti+1]); @@ -60,11 +60,10 @@ ll op_inclusive(int l, int r, int n) { r++; int tl = l += n, tr = r += n, sz = 1; push(tl); push(tr); - ll ans = NEUTRAL; + ll left = NEUTRAL, righ = NEUTRAL; for (; l < r; l /= 2, r /= 2, sz *= 2) { - if (l & 1) ans = OP(ans, apply(l++, dlta(), sz)); - if (r & 1) ans = OP(ans, apply(--r, dlta(), sz)); + if (l & 1) left = OP(left, apply(l++, dlta(), sz)); + if (r & 1) righ = OP(apply(--r, dlta(), sz), righ); } - pull(tl); pull(tr); - return ans; + return OP(left, righ); } diff --git a/fontes/number-theoretic-transform.h b/fontes/number-theoretic-transform.h new file mode 100644 index 0000000000000000000000000000000000000000..db36cc319de5b6d16651d102429c56d6c5b93024 --- /dev/null +++ b/fontes/number-theoretic-transform.h @@ -0,0 +1,32 @@ +void ntt (vector<mint>& a, bool rev) { + int n = a.size(); auto b = a; + mint g = 1; + while (binary_pow(g, P/2) == 1) g += 1; + if (rev) { g = binary_pow(g.x, P-2); } + + for (int st = n/2; st; st /= 2) { + mint w = binary_pow(g, P/(n/st)), wn = 1; + for (int i = 0; i < n/2; i += st) { + for (int j = 0; j < st; j++) { + auto u = a[2*i+j], v = wn * a[2*i+j+st]; + b[i+j] = u + v; b[i+n/2+j] = u - v; + } + wn *= w; + } + swap(a, b); + } + + mint n1 = binary_pow(n, P-2); + if (rev) for (mint& x : a) { x *= n1; } +} + +vector<mint> convolution (vector<mint>& a, vector<mint>& b) { + vector<mint> fa (all(a)), fb (all(b)); + int n = 1; + while (n < a.size() + b.size()) { n *= 2; } + fa.resize(n); fb.resize(n); + ntt(fa, 0); ntt(fb, 0); + for (int i = 0; i < n; i++) { fa[i] *= fb[i]; } + ntt(fa, 1); + return fa; +} diff --git a/fontes/rotating-calipers.h b/fontes/rotating-calipers.h new file mode 100644 index 0000000000000000000000000000000000000000..3c9a460f3cc8954565cb6474365021e05b25b121 --- /dev/null +++ b/fontes/rotating-calipers.h @@ -0,0 +1,23 @@ +ld abs_area(pt p, pt q, pt r) { + return abs((p.px*q.py + q.px*r.py + r.px*p.py) - + (p.py*q.px + q.py*r.px + r.py*p.px)); +} + +ld rotating_calipers(vector<pt>& ps) { + vector<pt> h = convex_hull(ps); int n = h.size(); + if (n == 1) { return 0; } + if (n == 2) { return abs(h[0] - h[1]); } + int k = 1; + while (abs_area(h[n-1], h[0], h[(k+1)%n]) > + abs_area(h[n-1], h[0], h[k])) k++; + ld ans = 0; + for (int i = 0, j = k; i <= k; i++) { + while (abs_area(h[i], h[(i+1)%n], h[(j+1)%n]) > + abs_area(h[i], h[(i+1)%n], h[j])) { + ans = max(ans, abs(h[i] - h[(j+1)%n])); + j = (j+1) % n; + } + ans = max(ans, abs(h[i] - h[j])); + } + return ans; +} diff --git a/fontes/segment-tree.h b/fontes/segment-tree.h index 1ed1d74754dd2c0ae6d903a26b7fd57cc6b5fb11..c9869545274e9d18f5f9b7e56331cd30f07bb009 100644 --- a/fontes/segment-tree.h +++ b/fontes/segment-tree.h @@ -1,20 +1,20 @@ vector<int> t (2*N); void build(vector<int>& src, int n) { - for (int i = 1; i < src.size(); i++) + for (int i = 1; i <= n; i++) t[n+i] = src[i]; - for (int i = n-1; i > 0; i--) - t[i] = OP(t[2*i], t[2*i+1]); + for (int ti = n-1; ti > 0; ti--) + t[ti] = OP(t[2*ti], t[2*ti+1]); } int op_inclusive(int l, int r, int n) { r++; - int left = NEUTRAL, right = NEUTRAL; + int left = NEUTRAL, righ = NEUTRAL; for (l += n, r += n; l < r; l /= 2, r /= 2) { if (l & 1) left = OP(left, t[l++]); - if (r & 1) right = OP(right, t[--r]); + if (r & 1) righ = OP(t[--r], righ); } - return OP(left, right); + return OP(left, righ); } void set_value(int i, int v, int n) { diff --git a/fontes/suffix-tree.h b/fontes/suffix-tree.h index cf5ae8a5d85d2c0e89c0ff375efebc536c32ef17..27a1fa2fce0cc7d0d82e68554a45aa10db72f97b 100644 --- a/fontes/suffix-tree.h +++ b/fontes/suffix-tree.h @@ -5,13 +5,13 @@ int at(string& s, int i, int j) { return id(s[l[i] + j]); } int cur = 1; -int new_node(int a, int b, int p) { +int mknode(int a, int b, int p) { l[cur] = a, r[cur] = b; parent[cur] = p; return cur++; } void build(string s) { s += '$'; - int root = new_node(0, -1, 0); + int root = mknode(0, -1, 0); int u = root, i = 0, ui = 0, ns = 0; for (int j = 0; j < s.size(); j++) for (; i <= j; i++) { if (ui == len(u) && nxt[u][id(s[j])]) @@ -19,14 +19,14 @@ void build(string s) { if (ui < len(u) && at(s, u, ui) == id(s[j])) { ui++; break; } if (ui == len(u)) { - nxt[u][id(s[j])] = new_node(j, s.size()-1, u); + nxt[u][id(s[j])] = mknode(j, s.size()-1, u); if (u != root) { u = suf[u]; ui = len(u); } } else { - int mi = new_node(l[u], l[u] + ui - 1, parent[u]); + int mi = mknode(l[u], l[u] + ui - 1, parent[u]); nxt[parent[u]][at(s, mi, 0)] = mi; nxt[mi][at(s, u, ui)] = u; parent[u] = mi; l[u] += ui; - nxt[mi][id(s[j])] = new_node(j, s.size()-1, mi); + nxt[mi][id(s[j])] = mknode(j, s.size()-1, mi); if (ns) { suf[ns] = mi; } u = parent[mi]; int g; if (u != root)