summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--datastructures/datastructures.tex62
-rw-r--r--datastructures/lazyPropagation.cpp9
-rw-r--r--datastructures/lichao.cpp76
-rw-r--r--datastructures/pbds.cpp18
-rw-r--r--datastructures/stlPriorityQueue.cpp8
-rw-r--r--geometry/geometry.tex17
-rw-r--r--graph/2sat.cpp16
-rw-r--r--graph/graph.tex133
-rw-r--r--graph/havelHakimi.cpp4
-rw-r--r--graph/maxWeightBipartiteMatching.cpp37
-rw-r--r--graph/minCostMaxFlow.cpp3
-rw-r--r--graph/pushRelabel.cpp151
-rw-r--r--graph/pushRelabel2.cpp66
-rw-r--r--graph/reroot.cpp62
-rw-r--r--graph/scc.cpp18
-rw-r--r--graph/virtualTree.cpp22
-rw-r--r--math/binomial0.cpp2
-rw-r--r--math/binomial1.cpp3
-rw-r--r--math/chineseRemainder.cpp41
-rw-r--r--math/extendedEuclid.cpp9
-rw-r--r--math/math.tex190
-rw-r--r--math/multInv.cpp7
-rw-r--r--math/rho.cpp4
-rw-r--r--math/shortModInv.cpp6
-rw-r--r--math/tables/composite.tex43
-rw-r--r--math/transforms/seriesOperations.cpp56
-rw-r--r--other/stuff.cpp3
-rw-r--r--string/ahoCorasick.cpp66
-rw-r--r--string/manacher.cpp39
-rw-r--r--string/string.tex11
-rw-r--r--tcr.pdfbin667178 -> 664604 bytes
-rw-r--r--tcr.tex3
32 files changed, 601 insertions, 584 deletions
diff --git a/datastructures/datastructures.tex b/datastructures/datastructures.tex
index 4139219..1ccefaa 100644
--- a/datastructures/datastructures.tex
+++ b/datastructures/datastructures.tex
@@ -14,6 +14,15 @@
\sourcecode{datastructures/lazyPropagation.cpp}
\end{algorithm}
+\begin{algorithm}{Wavelet Tree}
+ \begin{methods}
+ \method{Constructor}{baut den Baum auf}{n\*\log(n)}
+ \method{kth}{sort $[l, r)[k]$}{\log(n)}
+ \method{countSmaller}{Anzahl elemente in $[l, r)$ kleiner als $k$}{\log(n)}
+ \end{methods}
+ \sourcecode{datastructures/waveletTree.cpp}
+\end{algorithm}
+
\begin{algorithm}{Fenwick Tree}
\begin{methods}
\method{init}{baut den Baum auf}{n\*\log(n)}
@@ -29,11 +38,11 @@
\end{methods}
\sourcecode{datastructures/fenwickTree2.cpp}
\end{algorithm}
-\clearpage
\begin{algorithm}{STL-Rope (Implicit Cartesian Tree)}
\sourcecode{datastructures/stlRope.cpp}
\end{algorithm}
+\columnbreak
\begin{algorithm}{(Implicit) Treap (Cartesian Tree)}
\begin{methods}
@@ -54,15 +63,6 @@
\sourcecode{datastructures/sparseTable.cpp}
\end{algorithm}
-\begin{algorithm}{Wavelet Tree}
- \begin{methods}
- \method{Constructor}{baut den Baum auf}{n\*\log(n)}
- \method{kth}{sort $[l, r)[k]$}{\log(n)}
- \method{countSmaller}{Anzahl elemente in $[l, r)$ kleiner als $k$}{\log(n)}
- \end{methods}
- \sourcecode{datastructures/waveletTree.cpp}
-\end{algorithm}
-
\begin{algorithm}{STL-Bitset}
\sourcecode{datastructures/bitset.cpp}
\end{algorithm}
@@ -81,6 +81,23 @@
\end{algorithm}
\clearpage
+\begin{algorithm}{Lichao}
+ \sourcecode{datastructures/lichao.cpp}
+\end{algorithm}
+
+\begin{algorithm}{Policy Based Data Structures}
+ \textbf{Wichtig:} Verwende \code{p.swap(p2)} anstatt \code{swap(p, p2)}!
+ \sourcecode{datastructures/stlPriorityQueue.cpp}
+ \columnbreak
+ \sourcecode{datastructures/pbds.cpp}
+\end{algorithm}
+
+\begin{algorithm}{Lower/Upper Envelope (Convex Hull Optimization)}
+ Um aus einem lower envelope einen upper envelope zu machen (oder umgekehrt), einfach beim Einfügen der Geraden $m$ und $b$ negieren.
+ \sourcecode{datastructures/monotonicConvexHull.cpp}
+ \sourcecode{datastructures/dynamicConvexHull.cpp}
+\end{algorithm}
+
\begin{algorithm}{Union-Find}
\begin{methods}
\method{init}{legt $n$ einzelne Unions an}{n}
@@ -90,13 +107,7 @@
\end{methods}
\sourcecode{datastructures/unionFind.cpp}
\end{algorithm}
-
-\begin{algorithm}{Lower/Upper Envelope (Convex Hull Optimization)}
- Um aus einem lower envelope einen upper envelope zu machen (oder umgekehrt), einfach beim Einfügen der Geraden $m$ und $b$ negieren.
- \sourcecode{datastructures/monotonicConvexHull.cpp}
- \columnbreak
- \sourcecode{datastructures/dynamicConvexHull.cpp}
-\end{algorithm}
+\columnbreak
\begin{algorithm}{Persistent}
\begin{methods}
@@ -108,22 +119,6 @@
\sourcecode{datastructures/persistentArray.cpp}
\end{algorithm}
-\begin{algorithm}{STL-Tree}
- \sourcecode{datastructures/stlTree.cpp}
-\end{algorithm}
-
-\begin{algorithm}{STL Priority Queue}
- Nicht notwendig, wenn Smaller-Larger-Optimization greift.
- \sourcecode{datastructures/stlPQ.cpp}
-\end{algorithm}
-
-\begin{algorithm}{STL HashMap}
- 3 bis 4 mal langsamer als \code{std::vector} aber 8 bis 9 mal schneller als \code{std::map}
- \sourcecode{datastructures/stlHashMap.cpp}
-\end{algorithm}
-
-
-
\begin{algorithm}[optional]{Range Minimum Query}
\begin{methods}
\method{init}{baut Struktur auf}{n\*\log(n)}
@@ -138,4 +133,3 @@
\end{methods}
\sourcecode{datastructures/firstUnused.cpp}
\end{algorithm}
-
diff --git a/datastructures/lazyPropagation.cpp b/datastructures/lazyPropagation.cpp
index 7af1a6a..0fe7bbe 100644
--- a/datastructures/lazyPropagation.cpp
+++ b/datastructures/lazyPropagation.cpp
@@ -65,13 +65,12 @@ struct SegTree {
ll lower_bound(int l, int r, T x) {
l += n, r += n;
push(l), push(r - 1);
- vector<int> a, st;
+ int a[64] = {}, lp = 0, rp = 64;
for (; l < r; l /= 2, r /= 2) {
- if (l&1) a.push_back(l++);
- if (r&1) st.push_back(--r);
+ if (l&1) a[lp++] = l++;
+ if (r&1) a[--rp] = --r;
}
- a.insert(a.end(), st.rbegin(), st.rend());
- for (int i : a) if (tree[i] >= x) { // Modify this
+ for (int i : a) if (i != 0 && tree[i] >= x) { // Modify this
while (i < n) {
push_down(i);
if (tree[2 * i] >= x) i = 2 * i; // And this
diff --git a/datastructures/lichao.cpp b/datastructures/lichao.cpp
index e12d243..f66778e 100644
--- a/datastructures/lichao.cpp
+++ b/datastructures/lichao.cpp
@@ -1,44 +1,46 @@
-vector<ll> xs; // IMPORTANT: Initialize before constructing Lichao!
-int findX(int i) {return distance(xs.begin(), lower_bound(all(xs), i));}
+vector<ll> xs; // IMPORTANT: Initialize before constructing!
+int findX(int i) {return lower_bound(all(xs), i) - begin(xs);}
-struct Fun{ // Default: Linear function. Change it to needed function type.
- ll m, c;
- ll operator()(int x) {return m*xs[x] + c;}
+struct Fun { // Default: Linear function. Change as needed.
+ ll m, c;
+ ll operator()(int x) {return m*xs[x] + c;}
};
// Default: Computes min. Change lines with comment for max.
-struct Lichao{
- static constexpr Fun id = {0, inf}; // {0, -inf}
- int n, cap;
- vector<Fun> seg;
- Lichao() : n(sz(xs)), cap(2<<__lg(n)), seg(2*cap, id) {}
-
- void _insert(Fun f, int l, int r, int i) {
- while (i < 2*cap){
- int m = (l+r)/2;
- if (m >= n) {r = m; i = 2*i; continue;}
- Fun &g = seg[i];
- if (f(m) < g(m)) swap(f, g); // >
- if (f(l) < g(l)) r = m, i = 2*i; // >
- else l = m, i = 2*i+1;
- }}
- void insert(Fun f) {_insert(f, 0, cap, 1);}
+struct Lichao {
+ static constexpr Fun id = {0, inf}; // {0, -inf}
+ int n, cap;
+ vector<Fun> seg;
+ Lichao() : n(sz(xs)), cap(2<<__lg(n)), seg(2*cap, id) {}
+
+ void _insert(Fun f, int l, int r, int i) {
+ while (i < 2*cap){
+ int m = (l+r)/2;
+ if (m >= n) {r = m; i = 2*i; continue;}
+ Fun &g = seg[i];
+ if (f(m) < g(m)) swap(f, g); // >
+ if (f(l) < g(l)) r = m, i = 2*i; // >
+ else l = m, i = 2*i+1;
+ }}
+ void insert(Fun f) {_insert(f, 0, cap, 1);}
- void _segmentInsert(Fun f, int l, int r, int a, int b, int i) {
- if (l <= a && b <= r) _insert(f, a, b, i);
- else if (a < r && l < b){
- int m = (a+b)/2;
- _segmentInsert(f, l, r, a, m, 2*i);
- _segmentInsert(f, l, r, m, b, 2*i+1);
- }}
- void segmentInsert(Fun f, ll l, ll r) {
- _segmentInsert(f, findX(l), findX(r), 0, cap, 1);
- }
+ void _segmentInsert(Fun f, int l, int r, int a, int b, int i) {
+ if (l <= a && b <= r) _insert(f, a, b, i);
+ else if (a < r && l < b){
+ int m = (a+b)/2;
+ _segmentInsert(f, l, r, a, m, 2*i);
+ _segmentInsert(f, l, r, m, b, 2*i+1);
+ }}
+ void segmentInsert(Fun f, ll l, ll r) {
+ _segmentInsert(f, findX(l), findX(r), 0, cap, 1);
+ }
- ll _query(int x) {
- ll ans = inf; // -inf
- for (int i = x + cap; i > 0; i /= 2) ans = min(ans, seg[i](x)); // max
- return ans;
- }
- ll query(ll x) {return _query(findX(x));}
+ ll _query(int x) {
+ ll ans = inf; // -inf
+ for (int i = x + cap; i > 0; i /= 2) {
+ ans = min(ans, seg[i](x)); // max
+ }
+ return ans;
+ }
+ ll query(ll x) {return _query(findX(x));}
};
diff --git a/datastructures/pbds.cpp b/datastructures/pbds.cpp
new file mode 100644
index 0000000..c2b44cc
--- /dev/null
+++ b/datastructures/pbds.cpp
@@ -0,0 +1,18 @@
+#include <ext/pb_ds/assoc_container.hpp>
+using namespace __gnu_pbds;
+template<typename T>
+using Tree = tree<T, null_type, less<T>, rb_tree_tag,
+ tree_order_statistics_node_update>;
+// T.order_of_key(x): number of elements strictly less than x
+// *T.find_by_order(k): k-th element
+
+template<typename T>
+struct chash {
+ const uint64_t C = ll(2e18 * acos(-1)) | 199; // random odd
+ size_t operator()(T o) const {
+ return __builtin_bswap64(hash<T>()(o) * C);
+}};
+template<typename K, typename V>
+using hashMap = gp_hash_table<K, V, chash<K>>;
+template<typename T>
+using hashSet = gp_hash_table<T, null_type, chash<T>>;
diff --git a/datastructures/stlPriorityQueue.cpp b/datastructures/stlPriorityQueue.cpp
new file mode 100644
index 0000000..32b2455
--- /dev/null
+++ b/datastructures/stlPriorityQueue.cpp
@@ -0,0 +1,8 @@
+#include <ext/pb_ds/priority_queue.hpp>
+template<typename T>
+using pQueue = __gnu_pbds::priority_queue<T>; //<T, greater<T>>
+
+auto it = pq.push(5);
+pq.modify(it, 6);
+pq.join(pq2);
+// push, join are O(1), pop, modify, erase O(log n) amortized
diff --git a/geometry/geometry.tex b/geometry/geometry.tex
index d3e1671..d753ed6 100644
--- a/geometry/geometry.tex
+++ b/geometry/geometry.tex
@@ -7,6 +7,14 @@
\sourcecode{geometry/closestPair.cpp}
\end{algorithm}
+\begin{algorithm}{Rotating calipers}
+ \begin{methods}
+ \method{antipodalPoints}{berechnet antipodale Punkte}{n}
+ \end{methods}
+ \textbf{WICHTIG:} Punkte müssen gegen den Uhrzeigersinn sortiert sein und konvexes Polygon bilden!
+ \sourcecode{geometry/antipodalPoints.cpp}
+\end{algorithm}
+
\begin{algorithm}{Konvexehülle}
\begin{methods}
\method{convexHull}{berechnet Konvexehülle}{n\*\log(n)}
@@ -19,15 +27,6 @@
\sourcecode{geometry/convexHull.cpp}
\end{algorithm}
-\columnbreak
-\begin{algorithm}{Rotating calipers}
- \begin{methods}
- \method{antipodalPoints}{berechnet antipodale Punkte}{n}
- \end{methods}
- \textbf{WICHTIG:} Punkte müssen gegen den Uhrzeigersinn sortiert sein und konvexes Polygon bilden!
- \sourcecode{geometry/antipodalPoints.cpp}
-\end{algorithm}
-
\subsection{Formeln~~--~\texttt{std::complex}}
\sourcecode{geometry/formulars.cpp}
\sourcecode{geometry/linesAndSegments.cpp}
diff --git a/graph/2sat.cpp b/graph/2sat.cpp
index 8fb3d39..75e54e6 100644
--- a/graph/2sat.cpp
+++ b/graph/2sat.cpp
@@ -2,7 +2,7 @@ struct sat2 {
int n; // + scc variablen
vector<int> sol;
- sat2(int vars) : n(vars*2), adj(vars*2) {};
+ sat2(int vars) : n(vars*2), adj(n) {}
static int var(int i) {return i << 1;} // use this!
@@ -18,20 +18,14 @@ struct sat2 {
void addAnd(int a, int b) {addTrue(a); addTrue(b);}
void addNand(int a, int b) {addOr(1^a, 1^b);}
- bool solvable() {
+ bool solve() {
scc(); //scc code von oben
+ sol.assign(n, -1);
for (int i = 0; i < n; i += 2) {
if (idx[i] == idx[i + 1]) return false;
+ sol[i] = idx[i] < idx[i + 1];
+ sol[i + 1] = !sol[i];
}
return true;
}
-
- void assign() {
- sol.assign(n, -1);
- for (int i = 0; i < sccCounter; i++) {
- if (sol[sccs[i][0]] == -1) {
- for (int v : sccs[i]) {
- sol[v] = 1;
- sol[1^v] = 0;
- }}}}
};
diff --git a/graph/graph.tex b/graph/graph.tex
index 6fbdb74..060d157 100644
--- a/graph/graph.tex
+++ b/graph/graph.tex
@@ -1,18 +1,31 @@
\section{Graphen}
-\begin{algorithm}{Eulertouren}
+\begin{algorithm}{Kruskal}
+ \begin{methods}[ll]
+ berechnet den Minimalen Spannbaum & \runtime{\abs{E}\cdot\log(\abs{E})} \\
+ \end{methods}
+ \sourcecode{graph/kruskal.cpp}
+\end{algorithm}
+
+\begin{algorithm}{Minimale Spannbäume}
+ \paragraph{Schnitteigenschaft}
+ Für jeden Schnitt $C$ im Graphen gilt:
+ Gibt es eine Kante $e$, die echt leichter ist als alle anderen Schnittkanten, so gehört diese zu allen minimalen Spannbäumen.
+ ($\Rightarrow$ Die leichteste Kante in einem Schnitt kann in einem minimalen Spannbaum verwendet werden.)
+
+ \paragraph{Kreiseigenschaft}
+ Für jeden Kreis $K$ im Graphen gilt:
+ Die schwerste Kante auf dem Kreis ist nicht Teil des minimalen Spannbaums.
+\end{algorithm}
+
+\begin{algorithm}{Heavy-Light Decomposition}
\begin{methods}
- \method{euler}{berechnet den Kreis}{\abs{V}+\abs{E}}
+ \method{get\_intervals}{gibt Zerlegung des Pfades von $u$ nach $v$}{\log(\abs{V})}
\end{methods}
- \sourcecode{graph/euler.cpp}
- \begin{itemize}
- \item Zyklus existiert, wenn jeder Knoten geraden Grad hat (ungerichtet),\\ bei jedem Knoten Ein- und Ausgangsgrad übereinstimmen (gerichtet).
- \item Pfad existiert, wenn genau $\{0, 2\}$ Knoten ungeraden Grad haben (ungerichtet),\\ bei allen Knoten Ein- und Ausgangsgrad übereinstimmen oder einer eine Ausgangskante mehr hat (Startknoten) und einer eine Eingangskante mehr hat (Endknoten).
- \item \textbf{Je nach Aufgabenstellung überprüfen, wie ein unzusammenhängender Graph interpretiert werden sollen.}
- \item Wenn eine bestimmte Sortierung verlangt wird oder Laufzeit vernachlässigbar ist, ist eine Implementierung mit einem \code{vector<set<int>> adj} leichter
- \item \textbf{Wichtig:} Algorithmus schlägt nicht fehl, falls kein Eulerzyklus existiert.
- Die Existenz muss separat geprüft werden.
- \end{itemize}
+ \textbf{Wichtig:} Intervalle sind halboffen
+
+ Subbaum unter dem Knoten $v$ ist das Intervall $[\text{\code{in[v]}},~\text{\code{out[v]}})$.
+ \sourcecode{graph/hld.cpp}
\end{algorithm}
\begin{algorithm}{Lowest Common Ancestor}
@@ -31,16 +44,20 @@
\sourcecode{graph/centroid.cpp}
\end{algorithm}
-\begin{algorithm}{Heavy-Light Decomposition}
+\begin{algorithm}{Eulertouren}
\begin{methods}
- \method{get\_intervals}{gibt Zerlegung des Pfades von $u$ nach $v$}{\log(\abs{V})}
+ \method{euler}{berechnet den Kreis}{\abs{V}+\abs{E}}
\end{methods}
- \textbf{Wichtig:} Intervalle sind halboffen
-
- Subbaum unter dem Knoten $v$ ist das Intervall $[\text{\code{in[v]}},~\text{\code{out[v]}})$.
- \sourcecode{graph/hld.cpp}
+ \sourcecode{graph/euler.cpp}
+ \begin{itemize}
+ \item Zyklus existiert, wenn jeder Knoten geraden Grad hat (ungerichtet),\\ bei jedem Knoten Ein- und Ausgangsgrad übereinstimmen (gerichtet).
+ \item Pfad existiert, wenn genau $\{0, 2\}$ Knoten ungeraden Grad haben (ungerichtet),\\ bei allen Knoten Ein- und Ausgangsgrad übereinstimmen oder einer eine Ausgangskante mehr hat (Startknoten) und einer eine Eingangskante mehr hat (Endknoten).
+ \item \textbf{Je nach Aufgabenstellung überprüfen, wie ein unzusammenhängender Graph interpretiert werden sollen.}
+ \item Wenn eine bestimmte Sortierung verlangt wird oder Laufzeit vernachlässigbar ist, ist eine Implementierung mit einem \code{vector<set<int>> adj} leichter
+ \item \textbf{Wichtig:} Algorithmus schlägt nicht fehl, falls kein Eulerzyklus existiert.
+ Die Existenz muss separat geprüft werden.
+ \end{itemize}
\end{algorithm}
-\clearpage
\begin{algorithm}{Baum-Isomorphie}
\begin{methods}
@@ -49,24 +66,6 @@
\sourcecode{graph/treeIsomorphism.cpp}
\end{algorithm}
-\begin{algorithm}{Minimale Spannbäume}
- \paragraph{Schnitteigenschaft}
- Für jeden Schnitt $C$ im Graphen gilt:
- Gibt es eine Kante $e$, die echt leichter ist als alle anderen Schnittkanten, so gehört diese zu allen minimalen Spannbäumen.
- ($\Rightarrow$ Die leichteste Kante in einem Schnitt kann in einem minimalen Spannbaum verwendet werden.)
-
- \paragraph{Kreiseigenschaft}
- Für jeden Kreis $K$ im Graphen gilt:
- Die schwerste Kante auf dem Kreis ist nicht Teil des minimalen Spannbaums.
-\end{algorithm}
-
-\begin{algorithm}{Kruskal}
- \begin{methods}[ll]
- berechnet den Minimalen Spannbaum & \runtime{\abs{E}\cdot\log(\abs{E})} \\
- \end{methods}
- \sourcecode{graph/kruskal.cpp}
-\end{algorithm}
-
\subsection{Kürzeste Wege}
\subsubsection{\textsc{Bellmann-Ford}-Algorithmus}
@@ -91,6 +90,14 @@ Sei $d_{i\smash{j}}$ die Distanzmatrix von $G$, dann gibt $d_{i\smash{j}}^k$ die
Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, dann gibt $a_{i\smash{j}}^k$ die Anzahl der Wege von $i$ nach $j$ mit Länge genau \textcolor{gray}{(maximal)} $k$ an mit der Verknüpfung: $c_{i\smash{j}} = a_{i\smash{j}} \otimes b_{i\smash{j}} = \sum a_{ik} + b_{k\smash{j}}$
+\begin{algorithm}{Dynamic Connectivity}
+ \begin{methods}
+ \method{Constructor}{erzeugt Baum ($n$ Knoten, $m$ updates)}{n+m}
+ \method{addEdge}{fügt Kannte ein,\code{id}=delete Zeitpunkt}{\log(n)}
+ \method{eraseEdge}{entfernt Kante \code{id}}{\log(n)}
+ \end{methods}
+ \sourcecode{graph/connect.cpp}
+\end{algorithm}
\begin{algorithm}{Erd\H{o}s-Gallai}
Sei $d_1 \geq \cdots \geq d_{n}$. Es existiert genau dann ein Graph $G$ mit Degreesequence $d$ falls $\sum\limits_{i=1}^{n} d_i$ gerade ist und für $1\leq k \leq n$: $\sum\limits_{i=1}^{k} d_i \leq k\cdot(k-1)+\sum\limits_{i=k+1}^{n} \min(d_i, k)$
@@ -100,13 +107,11 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\sourcecode{graph/havelHakimi.cpp}
\end{algorithm}
-\begin{algorithm}{Dynamic Connectivity}
+\begin{algorithm}{Strongly Connected Components (\textsc{Tarjan})}
\begin{methods}
- \method{Constructor}{erzeugt Baum ($n$ Knoten, $m$ updates)}{n+m}
- \method{addEdge}{fügt Kannte ein,\code{id}=delete Zeitpunkt}{\log(n)}
- \method{eraseEdge}{entfernt Kante \code{id}}{\log(n)}
+ \method{scc}{berechnet starke Zusammenhangskomponenten}{\abs{V}+\abs{E}}
\end{methods}
- \sourcecode{graph/connect.cpp}
+ \sourcecode{graph/scc.cpp}
\end{algorithm}
\begin{algorithm}{DFS}
@@ -121,13 +126,6 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\sourcecode{graph/articulationPoints.cpp}
\end{algorithm}
-\begin{algorithm}{Strongly Connected Components (\textsc{Tarjan})}
- \begin{methods}
- \method{scc}{berechnet starke Zusammenhangskomponenten}{\abs{V}+\abs{E}}
- \end{methods}
- \sourcecode{graph/scc.cpp}
-\end{algorithm}
-
\begin{algorithm}{2-SAT}
\sourcecode{graph/2sat.cpp}
\end{algorithm}
@@ -139,7 +137,6 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\end{methods}
\sourcecode{graph/bronKerbosch.cpp}
\end{algorithm}
-\clearpage
\begin{algorithm}{Cycle Counting}
\begin{methods}
@@ -164,6 +161,14 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\sourcecode{graph/blossom.cpp}
\end{algorithm}
+\begin{algorithm}{Rerooting Template}
+ \sourcecode{graph/reroot.cpp}
+\end{algorithm}
+
+\begin{algorithm}{Virtual Trees}
+ \sourcecode{graph/virtualTree.cpp}
+\end{algorithm}
+
\begin{algorithm}{Maximal Cardinatlity Bipartite Matching}
\label{kuhn}
\begin{methods}
@@ -179,13 +184,6 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\sourcecode{graph/hopcroftKarp.cpp}
\end{algorithm}
-\begin{algorithm}{Maximum Weight Bipartite Matching}
- \begin{methods}
- \method{match}{berechnet Matching}{\abs{V}^3}
- \end{methods}
- \sourcecode{graph/maxWeightBipartiteMatching.cpp}
-\end{algorithm}
-
\begin{algorithm}{Global Mincut}
\begin{methods}
\method{stoer\_wagner}{berechnet globalen Mincut}{\abs{V}\abs{E}+\abs{V}^2\*\log(\abs{E})}
@@ -205,12 +203,21 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\sourcecode{graph/capacityScaling.cpp}
}
+\optional{
\subsubsection{Push Relabel}
\begin{methods}
\method{maxFlow}{gut bei sehr dicht besetzten Graphen.}{\abs{V}^2\*\sqrt{\abs{E}}}
\method{addEdge}{fügt eine \textbf{gerichtete} Kante ein}{1}
\end{methods}
-\sourcecode{graph/pushRelabel2.cpp}
+\sourcecode{graph/pushRelabel.cpp}
+}
+
+\begin{algorithm}{Min-Cost-Max-Flow}
+ \begin{methods}
+ \method{mincostflow}{berechnet Fluss}{\abs{V}^2\cdot\abs{E}^2}
+ \end{methods}
+ \sourcecode{graph/minCostMaxFlow.cpp}
+\end{algorithm}
\subsubsection{Dinic's Algorithm mit Capacity Scaling}
\begin{methods}
@@ -218,6 +225,8 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\method{addEdge}{fügt eine \textbf{gerichtete} Kante ein}{1}
\end{methods}
\sourcecode{graph/dinicScaling.cpp}
+\vfill*
+\columnbreak
\optional{
\subsubsection{Anwendungen}
@@ -241,12 +250,15 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\end{itemize}
}
-\begin{algorithm}{Min-Cost-Max-Flow}
+\begin{algorithm}{Maximum Weight Bipartite Matching}
\begin{methods}
- \method{mincostflow}{berechnet Fluss}{\abs{V}^2\cdot\abs{E}^2}
+ \method{match}{berechnet Matching}{\abs{V}^3}
\end{methods}
- \sourcecode{graph/minCostMaxFlow.cpp}
+ \sourcecode{graph/maxWeightBipartiteMatching.cpp}
\end{algorithm}
+\vfill*
+\columnbreak
+
\begin{algorithm}[optional]{TSP}
\begin{methods}
@@ -261,3 +273,4 @@ Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, da
\end{methods}
\sourcecode{graph/bitonicTSPsimple.cpp}
\end{algorithm}
+
diff --git a/graph/havelHakimi.cpp b/graph/havelHakimi.cpp
index 6246fe0..cbd6991 100644
--- a/graph/havelHakimi.cpp
+++ b/graph/havelHakimi.cpp
@@ -1,6 +1,8 @@
vector<vector<int>> havelHakimi(const vector<int>& deg) {
priority_queue<pair<int, int>> pq;
- for (int i = 0; i < sz(deg); i++) pq.push({deg[i], i});
+ for (int i = 0; i < sz(deg); i++) {
+ if (deg[i] > 0) pq.push({deg[i], i});
+ }
vector<vector<int>> adj;
while (!pq.empty()) {
auto [degV, v] = pq.top(); pq.pop();
diff --git a/graph/maxWeightBipartiteMatching.cpp b/graph/maxWeightBipartiteMatching.cpp
index 5eda19b..a2b0a80 100644
--- a/graph/maxWeightBipartiteMatching.cpp
+++ b/graph/maxWeightBipartiteMatching.cpp
@@ -4,15 +4,14 @@ double costs[N_LEFT][N_RIGHT];
double match(int l, int r) {
vector<double> lx(l), ly(r);
//xy is matching from l->r, yx from r->l, or -1
- vector<int> xy(l, -1), yx(r, -1), augmenting(r);
- vector<bool> s(l);
+ vector<int> xy(l, -1), yx(r, -1);
vector<pair<double, int>> slack(r);
for (int x = 0; x < l; x++)
lx[x] = *max_element(costs[x], costs[x] + r);
for (int root = 0; root < l; root++) {
- augmenting.assign(r, -1);
- s.assign(l, false);
+ vector<int> aug(r, -1);
+ vector<bool> s(l);
s[root] = true;
for (int y = 0; y < r; y++) {
slack[y] = {lx[root] + ly[y] - costs[root][y], root};
@@ -22,38 +21,30 @@ double match(int l, int r) {
double delta = INF;
int x = -1;
for (int yy = 0; yy < r; yy++) {
- if (augmenting[yy] < 0) {
- if (slack[yy].first < delta) {
- delta = slack[yy].first;
- x = slack[yy].second;
- y = yy;
- }}}
+ if (aug[yy] < 0 && slack[yy].first < delta) {
+ tie(delta, x) = slack[yy];
+ y = yy;
+ }}
if (delta > 0) {
for (int x = 0; x < l; x++) if (s[x]) lx[x] -= delta;
for (int y = 0; y < r; y++) {
- if (augmenting[y] >= 0) ly[y] += delta;
+ if (aug[y] >= 0) ly[y] += delta;
else slack[y].first -= delta;
}}
- augmenting[y] = x;
+ aug[y] = x;
x = yx[y];
- if (x == -1) break;
+ if (x < 0) break;
s[x] = true;
for (int y = 0; y < r; y++) {
- if (augmenting[y] < 0) {
+ if (aug[y] < 0) {
double alt = lx[x] + ly[y] - costs[x][y];
if (slack[y].first > alt) {
slack[y] = {alt, x};
}}}}
while (y >= 0) {
- // Jede Iteration vergrößert Matching um 1
- // (können 0-Kanten sein!)
- int x = augmenting[y];
- int prec = xy[x];
- yx[y] = x;
- xy[x] = y;
- y = prec;
+ yx[y] = aug[y];
+ swap(y, xy[aug[y]]);
}}
- // Wert des Matchings
return accumulate(all(lx), 0.0) +
- accumulate(all(ly), 0.0);
+ accumulate(all(ly), 0.0); // Wert des Matchings
}
diff --git a/graph/minCostMaxFlow.cpp b/graph/minCostMaxFlow.cpp
index 3526b17..14a222c 100644
--- a/graph/minCostMaxFlow.cpp
+++ b/graph/minCostMaxFlow.cpp
@@ -8,7 +8,6 @@ struct MinCostFlow {
vector<vector<int>> adj;
vector<int> pref, con;
vector<ll> dist;
-
const int s, t;
ll maxflow, mincost;
@@ -27,12 +26,10 @@ struct MinCostFlow {
dist.assign(sz(adj), INF);
vector<bool> inqueue(sz(adj));
queue<int> queue;
-
dist[s] = 0;
queue.push(s);
pref[s] = s;
inqueue[s] = true;
-
while (!queue.empty()) {
int cur = queue.front(); queue.pop();
inqueue[cur] = false;
diff --git a/graph/pushRelabel.cpp b/graph/pushRelabel.cpp
index 52ba8b1..904aec6 100644
--- a/graph/pushRelabel.cpp
+++ b/graph/pushRelabel.cpp
@@ -1,109 +1,64 @@
-constexpr ll inf = 1ll<<60;
-
struct Edge {
- int from, to;
+ int to, rev;
ll f, c;
};
-vector<Edge> edges;
-vector<vector<int>> adj, llist;
-vector<int> height, ccount, que;
-vector<ll> excess;
-vector<list<int>> dlist;
-vector<list<int>::iterator> iter;
-int highest, highestActive;
-
-void addEdge(int from, int to, ll c) {
- adj[from].push_back(sz(edges));
- edges.push_back({from, to, 0, c});
- adj[to].push_back(sz(edges));
- edges.push_back({to, from, 0, 0});
-}
+vector<vector<Edge>> adj;
+vector<vector<int>> hs;
+vector<ll> ec;
+vector<int> cur, H;
-void globalRelabel(int n, int t) {
- height.assign(n, n);
- height[t] = 0;
- ccount.assign(n, 0);
- que.assign(n+1, 0);
- int qh = 0, qt = 0;
- for (que[qt++] = t; qh < qt;) {
- int v = que[qh++], h = height[v] + 1;
- for (int id : adj[v]) {
- if (height[edges[id].to] == n && edges[id ^ 1].c - edges[id ^ 1].f > 0) {
- ccount[height[edges[id].to] = h]++;
- que[qt++] = edges[id].to;
- }}}
- llist.assign(n+1, {});
- dlist.assign(n+1, {});
- for (int v = 0; v < n; v++) {
- if (height[v] < n) {
- iter[v] = dlist[height[v]].insert(dlist[height[v]].begin(), v);
- if (excess[v] > 0) llist[height[v]].push_back(v);
- }}
- highest = highestActive = height[que[qt - 1]];
+void addEdge(int u, int v, ll c) {
+ adj[u].push_back({v, (int)sz(adj[v]), 0, c});
+ adj[v].push_back({u, (int)sz(adj[u])-1, 0, 0});
}
-void push(int v, int id) {
- int u = edges[id].to;
- ll df = min(excess[v], edges[id].c - edges[id].f);
- edges[id].f += df;
- edges[id^1].f -= df;
- excess[v] -= df;
- excess[u] += df;
- if (0 < excess[u] && excess[u] <= df) llist[height[u]].push_back(u);
+void addFlow(Edge& e, ll f) {
+ if (ec[e.to] == 0 && f > 0)
+ hs[H[e.to]].push_back(e.to);
+ e.f += f;
+ adj[e.to][e.rev].f -= f;
+ ec[e.to] += f;
+ ec[adj[e.to][e.rev].to] -= f;
}
-void discharge(int n, int v) {
- int nh = n;
- for (int id : adj[v]) {
- if (edges[id].c - edges[id].f > 0) {
- if (height[v] == height[edges[id].to] + 1) {
- push(v, id);
- if (!excess[v]) return;
- } else {
- nh = min(nh, height[edges[id].to] + 1);
- }}}
- int h = height[v];
- if (ccount[h] == 1) {
- for (int i = h; i <= highest; i++) {
- for (auto p : dlist[i]) --ccount[height[p]], height[p] = n;
- dlist[i].clear();
- }
- highest = h - 1;
- } else {
- --ccount[h], iter[v] = dlist[h].erase(iter[v]), height[v] = nh;
- if (nh == n) return;
- ++ccount[nh], iter[v] = dlist[nh].insert(dlist[nh].begin(), v);
- highest = max(highest, highestActive = nh);
- llist[nh].push_back(v);
-}}
-
ll maxFlow(int s, int t) {
int n = sz(adj);
- llist.assign(n + 1, {});
- dlist.assign(n + 1, {});
- highestActive = highest = 0;
- height.assign(n, 0);
- height[s] = n;
- iter.resize(n);
- for (int v = 0; v < n; v++) {
- if (v != s) iter[v] = dlist[height[v]].insert(dlist[height[v]].begin(), v);
- }
- ccount.assign(n, 0);
- ccount[0] = n-1;
- excess.assign(n, 0);
- excess[s] = inf;
- excess[t] = -inf;
- for (int id : adj[s]) push(s, id);
- globalRelabel(n, t);
- while (highestActive >= 0) {
- if (llist[highestActive].empty()) {
- highestActive--;
- continue;
- }
- int v = llist[highestActive].back();
- llist[highestActive].pop_back();
- discharge(n, v);
- }
- return excess[t] + inf;
-}
+ hs.assign(2*n, {});
+ ec.assign(n, 0);
+ cur.assign(n, 0);
+ H.assign(n, 0);
+ H[s] = n;
+ ec[t] = 1;//never set t to active...
+ vector<int> co(2*n);
+ co[0] = n - 1;
+ for (Edge& e : adj[s]) addFlow(e, e.c);
+ for (int hi = 0;;) {
+ while (hs[hi].empty()) if (!hi--) return -ec[s];
+ int v = hs[hi].back();
+ hs[hi].pop_back();
+ while (ec[v] > 0) {
+ if (cur[v] == sz(adj[v])) {
+ H[v] = 2*n;
+ for (int i = 0; i < sz(adj[v]); i++) {
+ Edge& e = adj[v][i];
+ if (e.c - e.f > 0 &&
+ H[v] > H[e.to] + 1) {
+ H[v] = H[e.to] + 1;
+ cur[v] = i;
+ }}
+ co[H[v]]++;
+ if (!--co[hi] && hi < n) {
+ for (int i = 0; i < n; i++) {
+ if (hi < H[i] && H[i] < n) {
+ co[H[i]]--;
+ H[i] = n + 1;
+ }}}
+ hi = H[v];
+ } else {
+ Edge& e = adj[v][cur[v]];
+ if (e.c - e.f > 0 && H[v] == H[e.to] + 1) {
+ addFlow(adj[v][cur[v]], min(ec[v], e.c - e.f));
+ } else {
+ cur[v]++;
+}}}}}
diff --git a/graph/pushRelabel2.cpp b/graph/pushRelabel2.cpp
deleted file mode 100644
index 7d7defd..0000000
--- a/graph/pushRelabel2.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-struct Edge {
- int from, to;
- ll f, c;
-};
-
-vector<Edge> edges;
-vector<vector<int>> adj, hs;
-vector<ll> ec;
-vector<int> cur, H;
-
-void addEdge(int from, int to, ll c) {
- adj[from].push_back(sz(edges));
- edges.push_back({from, to, 0, c});
- adj[to].push_back(sz(edges));
- edges.push_back({to, from, 0, 0});
-}
-
-void addFlow(int id, ll f) {
- if (ec[edges[id].to] == 0 && f > 0)
- hs[H[edges[id].to]].push_back(edges[id].to);
- edges[id].f += f;
- edges[id^1].f -= f;
- ec[edges[id].to] += f;
- ec[edges[id].from] -= f;
-}
-
-ll maxFlow(int s, int t) {
- int n = sz(adj);
- hs.assign(2*n, {});
- ec.assign(n, 0);
- cur.assign(n, 0);
- H.assign(n, 0);
- H[s] = n;
- ec[t] = 1;//never set t to active...
- vector<int> co(2*n);
- co[0] = n - 1;
- for (int id : adj[s]) addFlow(id, edges[id].c);
- for (int hi = 0;;) {
- while (hs[hi].empty()) if (!hi--) return -ec[s];
- int v = hs[hi].back();
- hs[hi].pop_back();
- while (ec[v] > 0) {
- if (cur[v] == sz(adj[v])) {
- H[v] = 2*n;
- for (int i = 0; i < sz(adj[v]); i++) {
- int id = adj[v][i];
- if (edges[id].c - edges[id].f > 0 &&
- H[v] > H[edges[id].to] + 1) {
- H[v] = H[edges[id].to] + 1;
- cur[v] = i;
- }}
- co[H[v]]++;
- if (!--co[hi] && hi < n) {
- for (int i = 0; i < n; i++) {
- if (hi < H[i] && H[i] < n) {
- co[H[i]]--;
- H[i] = n + 1;
- }}}
- hi = H[v];
- } else {
- auto e = edges[adj[v][cur[v]]];
- if (e.c - e.f > 0 && H[v] == H[e.to] + 1) {
- addFlow(adj[v][cur[v]], min(ec[v], e.c - e.f));
- } else {
- cur[v]++;
-}}}}}
diff --git a/graph/reroot.cpp b/graph/reroot.cpp
new file mode 100644
index 0000000..4c6a748
--- /dev/null
+++ b/graph/reroot.cpp
@@ -0,0 +1,62 @@
+// Usual Tree DP can be broken down in 4 steps:
+// - Initialize dp[v] = identity
+// - Iterate over all children w and take a value for w
+// by looking at dp[w] and possibly the edge label of v -> w
+// - combine the values of those children
+// usually this operation should be commutative and associative
+// - finalize the dp[v] after iterating over all children
+struct Reroot {
+ using T = ll;
+
+ // identity element
+ T E() {}
+ // x: dp value of child
+ // e: index of edge going to child
+ T takeChild(T x, int e) {}
+ T comb(T x, T y) {}
+ // called after combining all dp values of children
+ T fin(T x, int v) {}
+
+ vector<vector<pair<int, int>>> g;
+ vector<int> ord, pae;
+ vector<T> dp;
+
+ T dfs(int v) {
+ ord.push_back(v);
+ for (auto [w, e] : g[v]) {
+ g[w].erase(find(all(g[w]), pair(v, e^1)));
+ pae[w] = e^1;
+ dp[v] = comb(dp[v], takeChild(dfs(w), e));
+ }
+ return dp[v] = fin(dp[v], v);
+ }
+
+ vector<T> solve(int n, vector<pair<int, int>> edges) {
+ g.resize(n);
+ for (int i = 0; i < n-1; i++) {
+ g[edges[i].first].emplace_back(edges[i].second, 2*i);
+ g[edges[i].second].emplace_back(edges[i].first, 2*i+1);
+ }
+ pae.assign(n, -1);
+ dp.assign(n, E());
+ dfs(0);
+ vector<T> updp(n, E()), res(n, E());
+ for (int v : ord) {
+ vector<T> pref(sz(g[v])+1), suff(sz(g[v])+1);
+ if (v != 0) pref[0] = takeChild(updp[v], pae[v]);
+ for (int i = 0; i < sz(g[v]); i++){
+ auto [u, w] = g[v][i];
+ pref[i+1] = suff[i] = takeChild(dp[u], w);
+ pref[i+1] = comb(pref[i], pref[i+1]);
+ }
+ for (int i = sz(g[v])-1; i >= 0; i--) {
+ suff[i] = comb(suff[i], suff[i+1]);
+ }
+ for (int i = 0; i < sz(g[v]); i++) {
+ updp[g[v][i].first] = fin(comb(pref[i], suff[i+1]), v);
+ }
+ res[v] = fin(pref.back(), v);
+ }
+ return res;
+ }
+};
diff --git a/graph/scc.cpp b/graph/scc.cpp
index 1716add..ac9a40b 100644
--- a/graph/scc.cpp
+++ b/graph/scc.cpp
@@ -1,8 +1,7 @@
vector<vector<int>> adj, sccs;
-int counter, sccCounter;
+int counter;
vector<bool> inStack;
-// idx enthält den Index der SCC pro Knoten.
-vector<int> low, idx, s;
+vector<int> low, idx, s; //idx enthält Index der SCC pro Knoten.
void visit(int v) {
int old = low[v] = counter++;
@@ -15,14 +14,11 @@ void visit(int v) {
if (old == low[v]) {
sccs.push_back({});
- int u;
- do {
+ for (int u = -1; u != v;) {
u = s.back(); s.pop_back(); inStack[u] = false;
- idx[u] = sccCounter;
- sccs[sccCounter].push_back(u);
- } while (u != v);
- sccCounter++;
-}}
+ idx[u] = sz(sccs) - 1;
+ sccs.back().push_back(u);
+}}}
void scc() {
inStack.assign(sz(adj), false);
@@ -30,7 +26,7 @@ void scc() {
idx.assign(sz(adj), -1);
sccs.clear();
- counter = sccCounter = 0;
+ counter = 0;
for (int i = 0; i < sz(adj); i++) {
if (low[i] < 0) visit(i);
}}
diff --git a/graph/virtualTree.cpp b/graph/virtualTree.cpp
new file mode 100644
index 0000000..2fcea80
--- /dev/null
+++ b/graph/virtualTree.cpp
@@ -0,0 +1,22 @@
+// needs dfs in- and out- time and lca function
+vector<int> in, out;
+
+void virtualTree(vector<int> ind) { // indices of used nodes
+ sort(all(ind), [&](int x, int y) {return in[x] < in[y];});
+ for (int i=0; i<sz(a)-1; i++) {
+ ind.push_back(lca(ind[i], ind[i+1]));
+ }
+ sort(all(ind), [&](int x, int y) {return in[x] < in[y];});
+ ind.erase(unique(all(ind)), ind.end());
+
+ int n = ind.size();
+ vector<vector<int>> tree(n);
+ vector<int> st = {0};
+ for (int i=1; i<n; i++) {
+ while (in[ind[i]] >= out[ind[st.back()]]) st.pop_back();
+ tree[st.back()].push_back(i);
+ st.push(i);
+ }
+ // virtual directed tree with n nodes, original indices in ind
+ // weights can be calculated, e.g. with binary lifting
+}
diff --git a/math/binomial0.cpp b/math/binomial0.cpp
index d9af917..896a0f1 100644
--- a/math/binomial0.cpp
+++ b/math/binomial0.cpp
@@ -10,5 +10,5 @@ void precalc() {
ll calc_binom(ll n, ll k) {
if (n < 0 || n < k || k < 0) return 0;
- return (fac[n] * fac[n-k] % mod) * fac[k] % mod;
+ return (inv[n] * inv[n-k] % mod) * fac[k] % mod;
}
diff --git a/math/binomial1.cpp b/math/binomial1.cpp
index 02b27e3..dab20b3 100644
--- a/math/binomial1.cpp
+++ b/math/binomial1.cpp
@@ -2,8 +2,7 @@ ll calc_binom(ll n, ll k) {
if (k > n) return 0;
ll r = 1;
for (ll d = 1; d <= k; d++) {// Reihenfolge => Teilbarkeit
- r *= n--;
- r /= d;
+ r *= n--, r /= d;
}
return r;
}
diff --git a/math/chineseRemainder.cpp b/math/chineseRemainder.cpp
index 623f94b..ccbc5dc 100644
--- a/math/chineseRemainder.cpp
+++ b/math/chineseRemainder.cpp
@@ -1,35 +1,14 @@
-// Laufzeit: O(n * log(n)), n := Anzahl der Kongruenzen. Nur für
-// teilerfremde Moduli. Berechnet das kleinste, nicht negative x,
-// das alle Kongruenzen simultan löst. Alle Lösungen sind
-// kongruent zum kgV der Moduli (Produkt, da teilerfremd).
-struct ChineseRemainder {
+struct CRT {
using lll = __int128;
- vector<lll> lhs, rhs, mods, inv;
- lll M; // Produkt über die Moduli. Kann leicht überlaufen.
+ lll M = 1, sol = 0; // Solution unique modulo M
+ bool hasSol = true;
- ll g(const vector<lll> &vec) {
- lll res = 0;
- for (int i = 0; i < sz(vec); i++) {
- res += (vec[i] * inv[i]) % M;
- res %= M;
- }
- return res;
- }
-
- // Fügt Kongruenz l * x = r (mod m) hinzu.
- void addEquation(ll l, ll r, ll m) {
- lhs.push_back(l);
- rhs.push_back(r);
- mods.push_back(m);
- }
-
- ll solve() { // Löst das System.
- M = accumulate(all(mods), lll(1), multiplies<lll>());
- inv.resize(sz(lhs));
- for (int i = 0; i < sz(lhs); i++) {
- lll x = (M / mods[i]) % mods[i];
- inv[i] = (multInv(x, mods[i]) * (M / mods[i]));
- }
- return (multInv(g(lhs), M) * g(rhs)) % M;
+ // Adds congruence x = a (mod m)
+ void add(ll a, ll m) {
+ auto [d, s, t] = extendedEuclid(M, m);
+ if((a - sol) % d != 0) hasSol = false;
+ lll z = M/d * s;
+ M *= m/d;
+ sol = (z % M * (a-sol) % M + sol + M) % M;
}
};
diff --git a/math/extendedEuclid.cpp b/math/extendedEuclid.cpp
index d016a63..ecf4a16 100644
--- a/math/extendedEuclid.cpp
+++ b/math/extendedEuclid.cpp
@@ -1,7 +1,6 @@
// a*x + b*y = ggt(a, b)
-ll extendedEuclid(ll a, ll b, ll& x, ll& y) {
- if (a == 0) {x = 0; y = 1; return b;}
- ll x1, y1, d = extendedEuclid(b % a, a, x1, y1);
- x = y1 - (b / a) * x1; y = x1;
- return d;
+array<ll, 3> extendedEuclid(ll a, ll b) {
+ if (a == 0) return {b, 0, 1};
+ auto [d, x, y] = extendedEuclid(b % a, a);
+ return {d, y - (b / a) * x, x};
}
diff --git a/math/math.tex b/math/math.tex
index 8ccc55e..f9a5ea5 100644
--- a/math/math.tex
+++ b/math/math.tex
@@ -1,5 +1,12 @@
\section{Mathe}
+\begin{algorithm}{Zykel Erkennung}
+ \begin{methods}
+ \method{cycleDetection}{findet Zyklus von $x_0$ und Länge in $f$}{b+l}
+ \end{methods}
+ \sourcecode{math/cycleDetection.cpp}
+\end{algorithm}
+
\begin{algorithm}{Longest Increasing Subsequence}
\begin{itemize}
\item \code{lower\_bound} $\Rightarrow$ streng monoton
@@ -7,14 +14,6 @@
\end{itemize}
\sourcecode{math/longestIncreasingSubsequence.cpp}
\end{algorithm}
-\columnbreak
-
-\begin{algorithm}{Zykel Erkennung}
- \begin{methods}
- \method{cycleDetection}{findet Zyklus von $x_0$ und Länge in $f$}{b+l}
- \end{methods}
- \sourcecode{math/cycleDetection.cpp}
-\end{algorithm}
\begin{algorithm}{Permutationen}
\begin{methods}
@@ -44,21 +43,20 @@
\begin{algorithm}{ggT, kgV, erweiterter euklidischer Algorithmus}
\runtime{\log(a) + \log(b)}
- \sourcecode{math/gcd-lcm.cpp}
\sourcecode{math/extendedEuclid.cpp}
\end{algorithm}
-\subsection{Multiplikatives Inverses von $\boldsymbol{n}$ in $\boldsymbol{\mathbb{Z}/p\mathbb{Z}}$}
-\textbf{Falls $\boldsymbol{p}$ prim:}\quad $x^{-1} \equiv x^{p-2} \bmod p$
+\subsection{Multiplikatives Inverses von $\boldsymbol{x}$ in $\boldsymbol{\mathbb{Z}/m\mathbb{Z}}$}
+\textbf{Falls $\boldsymbol{m}$ prim:}\quad $x^{-1} \equiv x^{m-2} \bmod m$
-\textbf{Falls $\boldsymbol{\ggT(n, p) = 1}$:}
+\textbf{Falls $\boldsymbol{\ggT(x, m) = 1}$:}
\begin{itemize}
\item Erweiterter euklidischer Algorithmus liefert $\alpha$ und $\beta$ mit
- $\alpha n + \beta p = 1$.
- \item Nach Kongruenz gilt $\alpha n + \beta p \equiv \alpha n \equiv 1 \bmod p$.
- \item $n^{-1} :\equiv \alpha \bmod p$
+ $\alpha x + \beta m = 1$.
+ \item Nach Kongruenz gilt $\alpha x + \beta m \equiv \alpha x \equiv 1 \bmod m$.
+ \item $x^{-1} :\equiv \alpha \bmod m$
\end{itemize}
-\textbf{Sonst $\boldsymbol{\ggT(n, p) > 1}$:}\quad Es existiert kein $x^{-1}$.
+\textbf{Sonst $\boldsymbol{\ggT(x, m) > 1}$:}\quad Es existiert kein $x^{-1}$.
% \sourcecode{math/multInv.cpp}
\sourcecode{math/shortModInv.cpp}
@@ -121,19 +119,12 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch:
\sourcecode{math/divisors.cpp}
\end{algorithm}
-\begin{algorithm}{Primitivwurzeln}
- \begin{itemize}
- \item Primitivwurzel modulo $n$ existiert $\Leftrightarrow$ $n \in \{2,\ 4,\ p^\alpha,\ 2\cdot p^\alpha \mid\ 2 < p \in \mathbb{P},\ \alpha \in \mathbb{N}\}$
- \item es existiert entweder keine oder $\varphi(\varphi(n))$ inkongruente Primitivwurzeln
- \item Sei $g$ Primitivwurzel modulo $n$.
- Dann gilt:\newline
- Das kleinste $k$, sodass $g^k \equiv 1 \bmod n$, ist $k = \varphi(n)$.
- \end{itemize}
- \begin{methods}
- \method{isPrimitive}{prüft ob $g$ eine Primitivwurzel ist}{\log(\varphi(n))\*\log(n)}
- \method{findPrimitive}{findet Primitivwurzel (oder -1)}{\abs{ans}\*\log(\varphi(n))\*\log(n)}
- \end{methods}
- \sourcecode{math/primitiveRoot.cpp}
+\begin{algorithm}{Numerisch Extremstelle bestimmen}
+ \sourcecode{math/goldenSectionSearch.cpp}
+\end{algorithm}
+
+\begin{algorithm}{Numerisch Integrieren, Simpsonregel}
+ \sourcecode{math/simpson.cpp}
\end{algorithm}
\begin{algorithm}{Diskreter Logarithmus}
@@ -151,6 +142,22 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch:
\sourcecode{math/discreteNthRoot.cpp}
\end{algorithm}
+
+\begin{algorithm}{Primitivwurzeln}
+ \begin{itemize}
+ \item Primitivwurzel modulo $n$ existiert $\Leftrightarrow$ $n \in \{2,\ 4,\ p^\alpha,\ 2\cdot p^\alpha \mid\ 2 < p \in \mathbb{P},\ \alpha \in \mathbb{N}\}$
+ \item es existiert entweder keine oder $\varphi(\varphi(n))$ inkongruente Primitivwurzeln
+ \item Sei $g$ Primitivwurzel modulo $n$.
+ Dann gilt:\newline
+ Das kleinste $k$, sodass $g^k \equiv 1 \bmod n$, ist $k = \varphi(n)$.
+ \end{itemize}
+ \begin{methods}
+ \method{isPrimitive}{prüft ob $g$ eine Primitivwurzel ist}{\log(\varphi(n))\*\log(n)}
+ \method{findPrimitive}{findet Primitivwurzel (oder -1)}{\abs{ans}\*\log(\varphi(n))\*\log(n)}
+ \end{methods}
+ \sourcecode{math/primitiveRoot.cpp}
+\end{algorithm}
+
\begin{algorithm}{Linearessieb und Multiplikative Funktionen}
Eine (zahlentheoretische) Funktion $f$ heißt multiplikativ wenn $f(1)=1$ und $f(a\cdot b)=f(a)\cdot f(b)$, falls $\ggT(a,b)=1$.
@@ -185,7 +192,6 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch:
\end{itemize}
\end{algorithm}
-
\begin{algorithm}{Primzahlsieb von \textsc{Eratosthenes}}
\begin{itemize}
\item Bis $10^8$ in unter 64MB Speicher (lange Berechnung)
@@ -216,33 +222,25 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch:
%\sourcecode{math/mobius.cpp}
\end{algorithm}
-%\columnbreak
-%\subsection{\textsc{Euler}sche $\boldsymbol{\varphi}$-Funktion}
-%\begin{itemize}
-% \item Zählt die relativ primen Zahlen $\leq n$.
-%
-% \item Multiplikativ:
-% $\gcd(a,b) = 1 \Longrightarrow \varphi(a) \cdot \varphi(b) = \varphi(ab)$
-%
-% \item $p$ prim, $k \in \mathbb{N}$:
-% $~\varphi(p^k) = p^k - p^{k - 1}$
-%
-% \item \textbf{\textsc{Euler}'s Theorem:}
-% Für $b \geq \varphi(c)$ gilt: $a^b \equiv a^{b \bmod \varphi(c) + \varphi(c)} \pmod{c}$. Darüber hinaus gilt: $\gcd(a, c) = 1 \Leftrightarrow a^b \equiv a^{b \bmod \varphi(c)} \pmod{c}$.
-% Falls $m$ prim ist, liefert das den \textbf{kleinen Satz von \textsc{Fermat}}:
-% $a^{m} \equiv a \pmod{m}$
-%\end{itemize}
-%\sourcecode{math/phi.cpp}
-
-
-\begin{algorithm}{Numerisch Extremstelle bestimmen}
- \sourcecode{math/goldenSectionSearch.cpp}
-\end{algorithm}
-
-
-\begin{algorithm}{Numerisch Integrieren, Simpsonregel}
- \sourcecode{math/simpson.cpp}
-\end{algorithm}
+\optional{
+\columnbreak
+\subsection{\textsc{Euler}sche $\boldsymbol{\varphi}$-Funktion}
+\begin{itemize}
+ \item Zählt die relativ primen Zahlen $\leq n$.
+
+ \item Multiplikativ:
+ $\gcd(a,b) = 1 \Longrightarrow \varphi(a) \cdot \varphi(b) = \varphi(ab)$
+
+ \item $p$ prim, $k \in \mathbb{N}$:
+ $~\varphi(p^k) = p^k - p^{k - 1}$
+
+ \item \textbf{\textsc{Euler}'s Theorem:}
+ Für $b \geq \varphi(c)$ gilt: $a^b \equiv a^{b \bmod \varphi(c) + \varphi(c)} \pmod{c}$. Darüber hinaus gilt: $\gcd(a, c) = 1 \Leftrightarrow a^b \equiv a^{b \bmod \varphi(c)} \pmod{c}$.
+ Falls $m$ prim ist, liefert das den \textbf{kleinen Satz von \textsc{Fermat}}:
+ $a^{m} \equiv a \pmod{m}$
+\end{itemize}
+\sourcecode{math/phi.cpp}
+}
\begin{algorithm}{Polynome, FFT, NTT \& andere Transformationen}
Multipliziert Polynome $A$ und $B$.
@@ -259,21 +257,53 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch:
%\textcolor{safeOrange}{$\blacksquare$} NTT code, %\textcolor{safeGreen}{$\blacksquare$} FFT code
\sourcecode{math/transforms/fft.cpp}
\sourcecode{math/transforms/ntt.cpp}
+ \vfill*
+ \columnbreak
\sourcecode{math/transforms/bitwiseTransforms.cpp}
Multiplikation mit 2 transforms statt 3: (nur benutzten wenn nötig!)
\sourcecode{math/transforms/fftMul.cpp}
\end{algorithm}
-\subsection{LGS über $\boldsymbol{\mathbb{R}}$}
-\method{gauss}{löst LGS}{n^3}
-\sourcecode{math/gauss.cpp}
+\begin{algorithm}{Operations on Formal Power Series}
+ \sourcecode{math/transforms/seriesOperations.cpp}
+\end{algorithm}
\subsection{LGS über $\boldsymbol{\mathbb{F}_p}$}
\method{gauss}{löst LGS}{n^3}
\sourcecode{math/lgsFp.cpp}
-\clearpage
+\subsection{LGS über $\boldsymbol{\mathbb{R}}$}
+\method{gauss}{löst LGS}{n^3}
+\sourcecode{math/gauss.cpp}
+
+\begin{algorithm}{\textsc{Legendre}-Symbol}
+ Sei $p \geq 3$ eine Primzahl, $a \in \mathbb{Z}$:
+ \begin{align*}
+ \legendre{a}{p} &=
+ \begin{cases*}
+ \hphantom{-}0 & falls $p~\vert~a$ \\[-1ex]
+ \hphantom{-}1 & falls $\exists x \in \mathbb{Z}\backslash p\mathbb{Z} : a \equiv x^2 \bmod p$ \\[-1ex]
+ -1 & sonst
+ \end{cases*} \\
+ \legendre{-1}{p} = (-1)^{\frac{p - 1}{2}} &=
+ \begin{cases*}
+ \hphantom{-}1 & falls $p \equiv 1 \bmod 4$ \\[-1ex]
+ -1 & falls $p \equiv 3 \bmod 4$
+ \end{cases*} \\
+ \legendre{2}{p} = (-1)^{\frac{p^2 - 1}{8}} &=
+ \begin{cases*}
+ \hphantom{-}1 & falls $p \equiv \pm 1 \bmod 8$ \\[-1ex]
+ -1 & falls $p \equiv \pm 3 \bmod 8$
+ \end{cases*}
+ \end{align*}
+ \begin{align*}
+ \legendre{p}{q} \cdot \legendre{q}{p} = (-1)^{\frac{p - 1}{2} \cdot \frac{q - 1}{2}} &&
+ \legendre{a}{p} \equiv a^{\frac{p-1}{2}}\bmod p
+ \end{align*}
+ \sourcecode{math/legendre.cpp}
+\end{algorithm}
+\optional{
\subsection{Primzahlzählfunktion $\boldsymbol{\pi}$}
\begin{methods}
\method{init}{berechnet $\pi$ bis $N$}{N\*\log(\log(N))}
@@ -281,6 +311,7 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch:
\method{pi}{zählt Primzahlen $\leq n$ ($n < N^2$)}{n^{2/3}}
\end{methods}
\sourcecode{math/piLehmer.cpp}
+}
\begin{algorithm}{Lineare-Recurenz}
\begin{methods}
@@ -331,33 +362,6 @@ sich alle Lösungen von $x^2-ny^2=c$ berechnen durch:
\sourcecode{math/matrixPower.cpp}
\end{algorithm}
-\begin{algorithm}{\textsc{Legendre}-Symbol}
- Sei $p \geq 3$ eine Primzahl, $a \in \mathbb{Z}$:
- \begin{align*}
- \legendre{a}{p} &=
- \begin{cases*}
- \hphantom{-}0 & falls $p~\vert~a$ \\[-1ex]
- \hphantom{-}1 & falls $\exists x \in \mathbb{Z}\backslash p\mathbb{Z} : a \equiv x^2 \bmod p$ \\[-1ex]
- -1 & sonst
- \end{cases*} \\
- \legendre{-1}{p} = (-1)^{\frac{p - 1}{2}} &=
- \begin{cases*}
- \hphantom{-}1 & falls $p \equiv 1 \bmod 4$ \\[-1ex]
- -1 & falls $p \equiv 3 \bmod 4$
- \end{cases*} \\
- \legendre{2}{p} = (-1)^{\frac{p^2 - 1}{8}} &=
- \begin{cases*}
- \hphantom{-}1 & falls $p \equiv \pm 1 \bmod 8$ \\[-1ex]
- -1 & falls $p \equiv \pm 3 \bmod 8$
- \end{cases*}
- \end{align*}
- \begin{align*}
- \legendre{p}{q} \cdot \legendre{q}{p} = (-1)^{\frac{p - 1}{2} \cdot \frac{q - 1}{2}} &&
- \legendre{a}{p} \equiv a^{\frac{p-1}{2}}\bmod p
- \end{align*}
- \sourcecode{math/legendre.cpp}
-\end{algorithm}
-
\begin{algorithm}{Inversionszahl}
\sourcecode{math/inversions.cpp}
\end{algorithm}
@@ -405,20 +409,20 @@ so gilt
%\begin{algorithm}{Binomialkoeffizienten}
\paragraph{Binomialkoeffizienten}
Die Anzahl der \mbox{$k$-elementigen} Teilmengen einer \mbox{$n$-elementigen} Menge.
+
\begin{methods}
\method{precalc}{berechnet $n!$ und $n!^{-1}$ vor}{\mathit{lim}}
\method{calc\_binom}{berechnet Binomialkoeffizient}{1}
\end{methods}
\sourcecode{math/binomial0.cpp}
Falls $n >= p$ for $\mathit{mod}=p^k$ berechne \textit{fac} und \textit{inv} aber teile $p$ aus $i$ und berechne die häufigkeit von $p$ in $n!$ als $\sum\limits_{i=1}\big\lfloor\frac{n}{p^i}\big\rfloor$
-\columnbreak
-
- \begin{methods}
+
+ \begin{methods}
\method{calc\_binom}{berechnet Binomialkoeffizient $(n \le 61)$}{k}
\end{methods}
\sourcecode{math/binomial1.cpp}
-
- \begin{methods}
+
+ \begin{methods}
\method{calc\_binom}{berechnet Binomialkoeffizient modulo Primzahl $p$}{p-n}
\end{methods}
\sourcecode{math/binomial3.cpp}
diff --git a/math/multInv.cpp b/math/multInv.cpp
index 87603f3..647dc2d 100644
--- a/math/multInv.cpp
+++ b/math/multInv.cpp
@@ -1,5 +1,4 @@
-ll multInv(ll n, ll p) {
- ll x, y;
- extendedEuclid(n, p, x, y); // Implementierung von oben.
- return ((x % p) + p) % p;
+ll multInv(ll x, ll m) {
+ auto [d, a, b] = extendedEuclid(x, m); // Implementierung von oben.
+ return ((a % m) + m) % m;
}
diff --git a/math/rho.cpp b/math/rho.cpp
index 635c630..7885196 100644
--- a/math/rho.cpp
+++ b/math/rho.cpp
@@ -1,8 +1,8 @@
using lll = __int128;
ll rho(ll n) { // Findet Faktor < n, nicht unbedingt prim.
if (n % 2 == 0) return 2;
- ll x = 0, y = 0, prd = 2;
- auto f = [n](lll x){return (x * x) % n + 1;};
+ ll x = 0, y = 0, prd = 2, i = n/2 + 7;
+ auto f = [&](lll x){return (x * x + i) % n;};
for (ll t = 30, i = n/2 + 7; t % 40 || gcd(prd, n) == 1; t++) {
if (x == y) x = ++i, y = f(x);
if (ll q = (lll)prd * abs(x-y) % n; q) prd = q;
diff --git a/math/shortModInv.cpp b/math/shortModInv.cpp
index 747eb7a..244bacf 100644
--- a/math/shortModInv.cpp
+++ b/math/shortModInv.cpp
@@ -1,3 +1,3 @@
-ll inv(ll a, ll b){ // a^{-1} mod b
- return 1 < a ? b - inv(b % a, a) * b / a : 1;
-} \ No newline at end of file
+ll multInv(ll x, ll m) { // x^{-1} mod m
+ return 1 < x ? m - inv(m % x, x) * m / x : 1;
+}
diff --git a/math/tables/composite.tex b/math/tables/composite.tex
index b8f5428..8e14b2e 100644
--- a/math/tables/composite.tex
+++ b/math/tables/composite.tex
@@ -1,26 +1,27 @@
-\begin{tabularx}{\linewidth}{|r||r|r||r|}
+
+\begin{tabularx}{\linewidth}{|r||r|r||r|r|r||C|}
\hline
- \multicolumn{4}{|c|}{Important Numbers} \\
+ \multicolumn{7}{|c|}{Important Numbers} \\
\hline
- $10^x$ & Highly Composite & Divs & Prime \\
+ $10^x$ & Highly Composite & \# Divs & $<$ Prime & $>$ Prime & \# Primes & \\
\hline
- $1$ & $6$ & $4$ & $10^{1}-~~3$ \\
- $2$ & $60$ & $12$ & $10^{2}-~~3$ \\
- $3$ & $840$ & $32$ & $10^{3}-~~3$ \\
- $4$ & $7~560$ & $64$ & $10^{4}- 27$ \\
- $5$ & $83~160$ & $128$ & $10^{5}-~~9$ \\
- $6$ & $720~720$ & $240$ & $10^{6}- 17$ \\
- $7$ & $8~648~640$ & $448$ & $10^{7}-~~9$ \\
- $8$ & $73~513~440$ & $768$ & $10^{8}- 11$ \\
- $9$ & $735~134~400$ & $1~344$ & $10^{9}- 63$ \\
- $10$ & $6~983~776~800$ & $2~304$ & $10^{10}- 33$ \\
- $11$ & $97~772~875~200$ & $4~032$ & $10^{11}- 23$ \\
- $12$ & $963~761~198~400$ & $6~720$ & $10^{12}- 11$ \\
- $13$ & $9~316~358~251~200$ & $10~752$ & $10^{13}- 29$ \\
- $14$ & $97~821~761~637~600$ & $17~280$ & $10^{14}- 27$ \\
- $15$ & $866~421~317~361~600$ & $26~880$ & $10^{15}- 11$ \\
- $16$ & $8~086~598~962~041~600$ & $41~472$ & $10^{16}- 63$ \\
- $17$ & $74~801~040~398~884~800$ & $64~512$ & $10^{17}-~~3$ \\
- $18$ & $897~612~484~786~617~600$ & $103~680$ & $10^{18}- 11$ \\
+ 1 & 6 & 4 & $-3$ & $+1$ & 4 & \\
+ 2 & 60 & 12 & $-3$ & $+1$ & 25 & \\
+ 3 & 840 & 32 & $-3$ & $+9$ & 168 & \\
+ 4 & 7\,560 & 64 & $-27$ & $+7$ & 1\,229 & \\
+ 5 & 83\,160 & 128 & $-9$ & $+3$ & 9\,592 & \\
+ 6 & 720\,720 & 240 & $-17$ & $+3$ & 78\,498 & \\
+ 7 & 8\,648\,640 & 448 & $-9$ & $+19$ & 664\,579 & \\
+ 8 & 73\,513\,440 & 768 & $-11$ & $+7$ & 5\,761\,455 & \\
+ 9 & 735\,134\,400 & 1\,344 & $-63$ & $+7$ & 50\,847\,534 & \\
+ 10 & 6\,983\,776\,800 & 2\,304 & $-33$ & $+19$ & 455\,052\,511 & \\
+ 11 & 97\,772\,875\,200 & 4\,032 & $-23$ & $+3$ & 4\,118\,054\,813 & \\
+ 12 & 963\,761\,198\,400 & 6\,720 & $-11$ & $+39$ & 37\,607\,912\,018 & \\
+ 13 & 9\,316\,358\,251\,200 & 10\,752 & $-29$ & $+37$ & 346\,065\,536\,839 & \\
+ 14 & 97\,821\,761\,637\,600 & 17\,280 & $-27$ & $+31$ & 3\,204\,941\,750\,802 & \\
+ 15 & 866\,421\,317\,361\,600 & 26\,880 & $-11$ & $+37$ & 29\,844\,570\,422\,669 & \\
+ 16 & 8\,086\,598\,962\,041\,600 & 41\,472 & $-63$ & $+61$ & 279\,238\,341\,033\,925 & \\
+ 17 & 74\,801\,040\,398\,884\,800 & 64\,512 & $-3$ & $+3$ & 2\,623\,557\,157\,654\,233 & \\
+ 18 & 897\,612\,484\,786\,617\,600 & 103\,680 & $-11$ & $+3$ & 24\,739\,954\,287\,740\,860 & \\
\hline
\end{tabularx}
diff --git a/math/transforms/seriesOperations.cpp b/math/transforms/seriesOperations.cpp
new file mode 100644
index 0000000..4743674
--- /dev/null
+++ b/math/transforms/seriesOperations.cpp
@@ -0,0 +1,56 @@
+vector<ll> poly_inv(const vector<ll>& a, int n) {
+ vector<ll> q = {powMod(a[0], mod-2, mod)};
+ for (int len = 1; len < n; len *= 2){
+ vector<ll> a2 = a, q2 = q;
+ a2.resize(2*len), q2.resize(2*len);
+ ntt(q2);
+ for (int j : {0, 1}) {
+ ntt(a2);
+ for (int i = 0; i < 2*len; i++) a2[i] = a2[i]*q2[i] % mod;
+ ntt(a2, true);
+ for (int i = 0; i < len; i++) a2[i] = 0;
+ }
+ for (int i = len; i < min(n, 2*len); i++) {
+ q.push_back((mod - a2[i]) % mod);
+ }}
+ return q;
+}
+
+vector<ll> poly_deriv(vector<ll> a) {
+ for (int i = 1; i < sz(a); i++)
+ a[i-1] = a[i] * i % mod;
+ a.pop_back();
+ return a;
+}
+
+vector<ll> poly_integr(vector<ll> a) {
+ if (a.empty()) return {0};
+ a.push_back(a.back() * powMod(sz(a), mod-2, mod) % mod);
+ for (int i = sz(a)-2; i > 0; i--)
+ a[i] = a[i-1] * powMod(i, mod-2, mod) % mod;
+ a[0] = 0;
+ return a;
+}
+
+vector<ll> poly_log(vector<ll> a, int n) {
+ a = mul(poly_deriv(a), poly_inv(a, n));
+ a.resize(n-1);
+ a = poly_integr(a);
+ return a;
+}
+
+vector<ll> poly_exp(vector<ll> a, int n) {
+ vector<ll> q = {1};
+ for (int len = 1; len < n; len *= 2) {
+ vector<ll> p = poly_log(q, 2*len);
+ for (int i = 0; i < 2*len; i++)
+ p[i] = (mod - p[i] + (i < sz(a) ? a[i] : 0)) % mod;
+ vector<ll> q2 = q;
+ q2.resize(2*len);
+ ntt(p), ntt(q2);
+ for (int i = 0; i < 2*len; i++) p[i] = p[i] * q2[i] % mod;
+ ntt(p, true);
+ for (int i = len; i < min(n, 2*len); i++) q.push_back(p[i]);
+ }
+ return q;
+}
diff --git a/other/stuff.cpp b/other/stuff.cpp
index 38fde78..41543ad 100644
--- a/other/stuff.cpp
+++ b/other/stuff.cpp
@@ -6,8 +6,7 @@ setxkbmap de
setxkbmap de,us -option grp:alt_space_toggle
// Schnelle Ein-/Ausgabe mit cin/cout.
-ios::sync_with_stdio(false);
-cin.tie(nullptr);
+cin.tie(nullptr)->ios::sync_with_stdio(false);
// Set mit eigener Sortierfunktion.
set<point2, decltype(comp)> set1(comp);
diff --git a/string/ahoCorasick.cpp b/string/ahoCorasick.cpp
index 9ffa6c9..eac312c 100644
--- a/string/ahoCorasick.cpp
+++ b/string/ahoCorasick.cpp
@@ -1,54 +1,52 @@
-constexpr ll ALPHABET_SIZE = 26;
-constexpr char OFFSET = 'a';
+constexpr ll ALPHABET_SIZE = 26, OFFSET = 'a';
struct AhoCorasick {
struct vert {
- int suffix, exit, character, parent;
- vector<int> nxt, patterns;
- vert(int c, int p) : suffix(-1), exit(-1),
- character(c), nxt(ALPHABET_SIZE, -1), parent(p) {}
- };
- vector<vert> aho;
+ int suffix = 0, ch, cnt = 0;
+ array<int, ALPHABET_SIZE> nxt = {};
- AhoCorasick() {aho.push_back(vert(-1, 0));}
+ vert(int p, int c) : suffix(-p), ch(c) {}
+ };
+ vector<vert> aho = {{0, -1}};
- // Call once for each pattern.
- void addString(string &s, int patternIdx) {
+ int addString(string &s) {
int v = 0;
- for (char c : s) {
+ for (auto c : s) {
int idx = c - OFFSET;
- if (aho[v].nxt[idx] == -1) {
+ if (!aho[v].nxt[idx]) {
aho[v].nxt[idx] = sz(aho);
- aho.emplace_back(idx, v);
+ aho.emplace_back(v, idx);
}
v = aho[v].nxt[idx];
}
- aho[v].patterns.push_back(patternIdx);
+ aho[v].cnt++;
+ return v; // trie node index of pattern (pattern state)
}
int getSuffix(int v) {
- if (aho[v].suffix == -1) {
- if (v == 0 || aho[v].parent == 0) aho[v].suffix = 0;
- else aho[v].suffix = go(getSuffix(aho[v].parent),
- aho[v].character);
+ if (aho[v].suffix < 0) {
+ aho[v].suffix = go(getSuffix(-aho[v].suffix), aho[v].ch);
}
return aho[v].suffix;
}
- int getExit(int v) {
- if (aho[v].exit == -1) {
- int suffix = getSuffix(v);
- if (v == 0) aho[v].exit = 0;
- else {
- if (aho[suffix].patterns.empty()) {
- aho[v].exit = getExit(suffix);
- } else {
- aho[v].exit = suffix;
- }}}
- return aho[v].exit;
- }
-
- int go(int v, int idx) { // Root is v=0.
- if (aho[v].nxt[idx] != -1) return aho[v].nxt[idx];
+ int go(int v, int idx) { // Root is v=0, idx is char - OFFSET
+ if (aho[v].nxt[idx]) return aho[v].nxt[idx];
else return v == 0 ? 0 : go(getSuffix(v), idx);
}
+
+ vector<vector<int>> adj;
+ vector<ll> dp;
+ void buildGraph() {
+ adj.resize(sz(aho));
+ dp.assign(sz(aho), 0);
+ for (int i = 1; i < sz(aho); i++) {
+ adj[getSuffix(i)].push_back(i);
+ }}
+
+ void dfs(int v = 0) { // dp on tree
+ for (int u : adj[v]) {
+ //dp[u] = dp[v] + aho[u].cnt; // pattern count
+ dfs(u);
+ dp[v] += dp[u]; // no of matches
+ }}
};
diff --git a/string/manacher.cpp b/string/manacher.cpp
index 03c06e1..112bd55 100644
--- a/string/manacher.cpp
+++ b/string/manacher.cpp
@@ -1,27 +1,20 @@
-string a, b; //a needs to be set
-vector<int> longest;
+vector<int> manacher(const string& t) {
+ //transforms "aa" to ".a.a." to find even length palindromes
+ string s(sz(t) * 2 + 1, '.');
+ for (int i = 0; i < sz(t); i++) s[2 * i + 1] = t[i];
-//transformes "aa" to ".a.a." to find even length palindromes
-void init() {
- b = string(sz(a) * 2 + 1, '.');
- longest.assign(sz(b), 0);
- for (int i = 0; i < sz(a); i++) {
- b[2 * i + 1] = a[i];
-}}
-
-void manacher() {
- int center = 0, last = 0, n = sz(b);
+ int mid = 0, r = 0, n = sz(s);
+ vector<int> pal(n);
for (int i = 1; i < n - 1; i++) {
- int i2 = 2 * center - i;
- longest[i] = (last > i) ? min(last - i, longest[i2]) : 0;
- while (i + longest[i] + 1 < n && i - longest[i] - 1 >= 0 &&
- b[i + longest[i] + 1] == b[i - longest[i] - 1]) {
- longest[i]++;
+ if (r > i) pal[i] = min(r - i, pal[2 * mid - i]);
+ while (pal[i] < min(i, n - i - 1) &&
+ s[i + pal[i] + 1] == s[i - pal[i] - 1]) {
+ pal[i]++;
}
- if (i + longest[i] > last) {
- center = i;
- last = i + longest[i];
- }}
- //convert lengths to string b (optional)
- for (int i = 0; i < n; i++) longest[i] = 2 * longest[i] + 1;
+ if (i + pal[i] > r) mid = i, r = i + pal[i];
+ }
+
+ //convert lengths to constructed string s (optional)
+ //for (int i = 0; i < n; i++) pal[i] = 2 * pal[i] + 1;
+ return pal;
}
diff --git a/string/string.tex b/string/string.tex
index c36ec69..393c2ad 100644
--- a/string/string.tex
+++ b/string/string.tex
@@ -47,18 +47,21 @@
\sourcecode{string/longestCommonSubsequence.cpp}
\end{algorithm}
+\columnbreak
\begin{algorithm}{\textsc{Aho-Corasick}-Automat}
\begin{methods}[ll]
- sucht patterns im Text & \runtime{\abs{Text}+\sum\abs{pattern}+\abs{matches}}
+ sucht patterns im Text & \runtime{\abs{Text}+\sum\abs{pattern}}
\end{methods}
\begin{enumerate}
- \item mit \code{addString(pattern, idx);} Patterns hinzufügen.
+ \item mit \code{addString(pattern, idx)} Patterns hinzufügen.
+ \item rufe \code{buildGraph()} auf
\item mit \code{state = go(state, idx)} in nächsten Zustand wechseln.
- \item mit \code{state = getExit(state)} den exit Kanten folgen bis \code{state == 0}
- \item dabei mit \code{aho[state].patterns} die Matches zählen
+ \item erhöhe dabei \code{dp[state]++}
+ \item rufe \code{dfs()} auf. In dp[pattern state] stehen die Anzahl der Matches
\end{enumerate}
\sourcecode{string/ahoCorasick.cpp}
\end{algorithm}
+\clearpage
\begin{algorithm}{Lyndon und De-Bruijn}
\begin{itemize}
diff --git a/tcr.pdf b/tcr.pdf
index e7330c0..63ebc4a 100644
--- a/tcr.pdf
+++ b/tcr.pdf
Binary files differ
diff --git a/tcr.tex b/tcr.tex
index 445f8b6..bfc73d1 100644
--- a/tcr.tex
+++ b/tcr.tex
@@ -3,7 +3,7 @@
\documentclass[a4paper,fontsize=7.8pt]{scrartcl}
% General information.
-\newcommand{\teamname}{Let's party!}
+\newcommand{\teamname}{Kindergarten Timelimit}
\newcommand{\university}{Karlsruhe Institute of Technology}
% Options
@@ -36,6 +36,7 @@
\tableofcontents
\end{multicols*}
}
+
\newpage
% Content.