diff options
| author | mzuenni <michi.zuendorf@gmail.com> | 2022-06-27 17:19:28 +0200 |
|---|---|---|
| committer | mzuenni <michi.zuendorf@gmail.com> | 2022-06-27 17:19:28 +0200 |
| commit | 5ab8a5088b729a9953b8dff1b2a985dc8fb2098b (patch) | |
| tree | ed40d6936c0e9eee40ba62751cbf99ecddbaddc2 | |
| parent | adabbad9c51cf7cd3874bfde8eac1fbcf84fec10 (diff) | |
updated tcr
185 files changed, 6175 insertions, 2732 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..21eab22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,222 @@ +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 + +## Intermediate documents: +*.dvi +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +*.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.gtex + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Comment the next line if you want to keep your tikz graphics files +*.tikz +*-tikzDictionary + +# listings +*.lol + +# makeidx +*.idx +*.ilg +*.ind +*.ist + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# nomencl +*.nlo + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# todonotes +*.tdo + +# easy-todo +*.lod + +# xindy +*.xdy + +# xypic precompiled matrices +*.xyc + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# Kile +*.backup + +# KBibTeX +*~[0-9]* + +# auto folder when using emacs and auctex +/auto/* + +# expex forward references with \gathertags +*-tags.tex + +*~ @@ -1,6 +1,5 @@ all: - pdflatex tcr.tex - pdflatex tcr.tex - make clean + latexmk -pdf tcr clean: - rm -f *~ .*~ *.aux *.log *.backup *.out *.backup *.bbl *.blg *.brf *.idx *.ilg *.ind *.thm *.toc *.fls
\ No newline at end of file + latexmk -c tcr + rm -f *.thm @@ -1,2 +1,3 @@ -# Team Contest Reference - ChaosKITs
-The team contest reference for team ChaosKITs from Karlsruhe, Germany.
+# Team Contest Reference
+a modified version of the team contest reference for team ChaosKITs from Karlsruhe, Germany.
+https://github.com/pjungeblut/ChaosKITs
diff --git a/datastructures/LCT.cpp b/datastructures/LCT.cpp new file mode 100644 index 0000000..fe92c7f --- /dev/null +++ b/datastructures/LCT.cpp @@ -0,0 +1,178 @@ +constexpr ll queryDefault = 0; +constexpr ll updateDefault = 0; + +ll _modify(ll x, ll y) { + return x + y; +} + +ll _query(ll x, ll y) { + return x + y; +} + +ll _update(ll delta, int length) { + if (delta == updateDefault) return updateDefault; + //ll result = delta + //for (int i=1; i<length; i++) result = _query(result, delta); + return delta * length; +} + +//generic: +ll joinValueDelta(ll value, ll delta) { + if (delta == updateDefault) return value; + return _modify(value, delta); +} + +ll joinDeltas(ll delta1, ll delta2) { + if (delta1 == updateDefault) return delta2; + if (delta2 == updateDefault) return delta1; + return _modify(delta1, delta2); +} + +struct LCT { + struct Node { + ll nodeValue, subTreeValue, delta; + bool revert; + int id, size; + Node *left, *right, *parent; + + Node(int id = 0, int val = queryDefault) : + nodeValue(val), subTreeValue(val), delta(updateDefault), + revert(false), id(id), size(1), + left(nullptr), right(nullptr), parent(nullptr) {} + + bool isRoot() { + return !parent || (parent->left != this && + parent->right != this); + } + + void push() { + if (revert) { + revert = false; + swap(left, right); + if (left) left->revert ^= 1; + if (right) right->revert ^= 1; + } + nodeValue = joinValueDelta(nodeValue, delta); + subTreeValue = joinValueDelta(subTreeValue, + _update(delta, size)); + if (left) left->delta = joinDeltas(left->delta, delta); + if (right) right->delta = joinDeltas(right->delta, delta); + delta = updateDefault; + } + + ll getSubtreeValue() { + return joinValueDelta(subTreeValue, _update(delta, size)); + } + + void update() { + subTreeValue = joinValueDelta(nodeValue, delta); + size = 1; + if (left) { + subTreeValue = _query(subTreeValue, + left->getSubtreeValue()); + size += left->size; + } + if (right) { + subTreeValue = _query(subTreeValue, + right->getSubtreeValue()); + size += right->size; + }} + }; + + vector<Node> nodes; + + LCT(int n) : nodes(n) { + for (int i = 0; i < n; i++) nodes[i].id = i; + } + + void connect(Node* ch, Node* p, int isLeftChild) { + if (ch) ch->parent = p; + if (isLeftChild >= 0) { + if (isLeftChild) p->left = ch; + else p->right = ch; + }} + + void rotate(Node* x) { + Node* p = x->parent; + Node* g = p->parent; + bool isRootP = p->isRoot(); + bool leftChildX = (x == p->left); + + connect(leftChildX ? x->right : x->left, p, leftChildX); + connect(p, x, !leftChildX); + connect(x, g, isRootP ? -1 : p == g->left); + p->update(); + } + + void splay(Node* x) { + while (!x->isRoot()) { + Node* p = x->parent; + Node* g = p->parent; + if (!p->isRoot()) g->push(); + p->push(); + x->push(); + if (!p->isRoot()) rotate((x == p->left) == + (p == g->left) ? p : x); + rotate(x); + } + x->push(); + x->update(); + } + + Node* expose(Node* x) { + Node* last = nullptr; + for (Node* y = x; y; y = y->parent) { + splay(y); + y->left = last; + last = y; + } + splay(x); + return last; + } + + void makeRoot(Node* x) { + expose(x); + x->revert ^= 1; + } + + bool connected(Node* x, Node* y) { + if (x == y) return true; + expose(x); + expose(y); + return x->parent; + } + + void link(Node* x, Node* y) { + assert(!connected(x, y)); // not yet connected! + makeRoot(x); + x->parent = y; + } + + void cut(Node* x, Node* y) { + makeRoot(x); + expose(y); + //must be a tree edge! + assert(!(y->right != x || x->left != nullptr)); + y->right->parent = nullptr; + y->right = nullptr; + } + + Node* lca(Node* x, Node* y) { + assert(connected(x, y)); + expose(x); + return expose(y); + } + + ll query(Node* from, Node* to) { + makeRoot(from); + expose(to); + if (to) return to->getSubtreeValue(); + return queryDefault; + } + + void modify(Node* from, Node* to, ll delta) { + makeRoot(from); + expose(to); + to->delta = joinDeltas(to->delta, delta); + } +};
\ No newline at end of file diff --git a/datastructures/RMQ.cpp b/datastructures/RMQ.cpp new file mode 100644 index 0000000..b38b838 --- /dev/null +++ b/datastructures/RMQ.cpp @@ -0,0 +1,27 @@ +vector<int> values; +vector<vector<int>> rmq; + +int select(int a, int b) { + return values[a] <= values[b] ? a : b; +} + +void build() { + for(int i = 0, s = 1, ss = 1; s <= values.size(); ss=s, s*=2, i++) { + for(int l = 0; l + s <= values.size(); l++) { + if(i == 0) rmq[0][l] = l; + else { + int r = l + ss; + rmq[i][l] = select(rmq[i-1][l], rmq[i-1][r]]); +}}}} + +void init(vector<int>& v) { + values = v; + rmq = vector<vector<int>>(floor(log2(values.size()))+1, vector<int>(values.size())); + build(); +} + +int query(int l, int r) { + if(l >= r) return l; + int s = floor(log2(r-l)); r = r - (1 << s); + return select(rmq[s][l],rmq[s][r]); +}
\ No newline at end of file diff --git a/datastructures/datastructures.tex b/datastructures/datastructures.tex index 1c529a4..8d1cb02 100644 --- a/datastructures/datastructures.tex +++ b/datastructures/datastructures.tex @@ -1,35 +1,131 @@ \section{Datenstrukturen} -\subsection{Union-Find} -\lstinputlisting{datastructures/unionFind.cpp} +\begin{algorithm}{Union-Find} + \begin{methods} + \method{init}{legt n einzelne Unions an}{n} + \method{findSet}{findet den Repräsentanten}{\log(n)} + \method{unionSets}{vereint 2 Mengen}{\log(n)} + \method{m\*findSet + n\*unionSets}{Folge von Befehlen}{n+m\*\alpha(n)} + \end{methods} + \sourcecode{datastructures/unionFind.cpp} +\end{algorithm} -\subsection{Segmentbaum} -\lstinputlisting{datastructures/segmentTree.cpp} +\begin{algorithm}{Segmentbaum} + \begin{methods} + \method{init}{baut den Baum auf}{n} + \method{query}{findet das min(max) in [l, r)}{\log(n)} + \method{update}{ändert einen Wert}{\log(n)} + \end{methods} + \sourcecode{datastructures/segmentTree.cpp} + + \subsubsection{Lazy Propagation} + Increment modifications, maximum queries + \sourcecode{datastructures/lazyPropagation1.cpp} + + Assignment modifications, sum queries + \sourcecode{datastructures/lazyPropagation2.cpp} +\end{algorithm} -\subsection{2D-Segmentbaum} -\lstinputlisting{datastructures/segmentTree2D.cpp} +\begin{algorithm}{Fenwick Tree} + \begin{methods} + \method{init}{baut den Baum auf}{n\*\log(n)} + \method{prefix\_sum}{summe von [0, i]}{\log(n)} + \method{update}{addiert ein Delta zu einem Element}{\log(n)} + \end{methods} + \sourcecode{datastructures/fenwickTree.cpp} + + \begin{methods} + \method{init}{baut den Baum auf}{n\*\log(n)} + \method{prefix\_sum}{summe von [0, i]}{\log(n)} + \method{update}{addiert ein Delta zu allen Elementen [l, r)}{\log(n)} + \end{methods} + \sourcecode{datastructures/fenwickTree2.cpp} +\end{algorithm} -\subsection{Fenwick Tree} -\lstinputlisting{datastructures/fenwickTree.cpp} -\lstinputlisting{datastructures/fenwickTreeNiklas.cpp} +\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} -\subsection{Sparse Table} -\lstinputlisting{datastructures/sparseTable.cpp} +\begin{algorithm}[optional]{Erste unbenutzte natürliche Zahl} + \begin{methods} + \method{get\_first\_unused}{findet kleinste unbenutzte Zahl}{\log(n)} + \end{methods} + \sourcecode{datastructures/firstUnused.cpp} +\end{algorithm} -\subsection{STL-Tree} -\lstinputlisting{datastructures/stlTree.cpp} +\begin{algorithm}{Treap (Cartesian Tree)} + \sourcecode{datastructures/treap.cpp} +\end{algorithm} -\subsection{STL-Rope (Implicit Cartesian Tree)} -\lstinputlisting{datastructures/stlRope.cpp} +\begin{algorithm}[optional]{Range Minimum Query} + \begin{methods} + \method{init}{baut Struktur auf}{n\*\log(n)} + \method{query}{Index des Minimums in [l, r)}{1} + \end{methods} + \sourcecode{datastructures/RMQ.cpp} +\end{algorithm} -\subsection{Treap (Cartesian Tree)} -\lstinputlisting{datastructures/treap.cpp} +\needspace{5\baselineskip}% +\begin{algorithm}{Range Minimum Query} + \begin{methods} + \method{init}{baut Struktur auf}{n\*\log(n)} + \method{queryIdempotent}{Index des Minimums in [l, r)}{1} + \end{methods} + \begin{itemize} + \item \code{better}-Funktion muss idempotent sein! + \end{itemize} + \sourcecode{datastructures/sparseTable.cpp} +\end{algorithm} -\subsection{STL Priority Queue} -Nicht notwendig, wenn Smaller-Larger-Optimization greift. -\lstinputlisting{datastructures/stlPQ.cpp} +\begin{algorithm}{STL-Tree} + \sourcecode{datastructures/stlTree.cpp} +\end{algorithm} -\subsection{Lower/Upper Envelop (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. -\lstinputlisting{datastructures/monotonicConvexHull.cpp} -\lstinputlisting{datastructures/dynamicConvexHull.cpp} +\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}{STL Priority Queue} + Nicht notwendig, wenn Smaller-Larger-Optimization greift. + \sourcecode{datastructures/stlPQ.cpp} +\end{algorithm} + +\begin{algorithm}{STL-Rope (Implicit Cartesian Tree)} + \sourcecode{datastructures/stlRope.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}{Link-Cut-Tree} + \begin{methods} + \method{Constructor}{baut Wald auf}{n} + \method{connected}{prüft ob zwei Knoten im selben baum liegen}{\log(n)} + \method{link}{fügt $\{x,y\}$ Kante ein}{\log(n)} + \method{cut}{entfernt $\{x,y\}$ Kante}{\log(n)} + \method{lca}{berechnet LCA von $x$ und $y$}{\log(n)} + \method{query}{berechnet \texttt{query} auf den Knoten des $xy$-Pfades}{\log(n)} + \method{modify}{erhöht jeden wert auf dem $xy$-Pfad}{\log(n)} + \end{methods} + \sourcecode{datastructures/LCT.cpp} +\end{algorithm} + +\clearpage +\begin{algorithm}{Persistent} + \begin{methods} + \method{get}{berechnet Wert zu Zeitpunkt $t$}{\log(t)} + \method{set}{ändert Wert zu Zeitpunkt $t$}{\log(t)} + \method{reset}{setzt die Datenstruktur auf Zeitpunkt $t$}{1} + \end{methods} + \sourcecode{datastructures/persistent.cpp} + \sourcecode{datastructures/persistentArray.cpp} +\end{algorithm} diff --git a/datastructures/dynamicConvexHull.cpp b/datastructures/dynamicConvexHull.cpp index 042d26c..18a46d0 100644 --- a/datastructures/dynamicConvexHull.cpp +++ b/datastructures/dynamicConvexHull.cpp @@ -1,35 +1,50 @@ // Upper Envelope, dynamisch. struct Line { - ll m, b; - mutable function<const Line*()> succ; - bool operator<(const Line& rhs) const { - if (rhs.b != LLONG_MIN) return m < rhs.m; - const Line* s = succ(); - if (!s) return 0; - ll x = rhs.m; - return b - s->b < (s->m - m) * x; - } + ll m, b; + mutable function<const Line*()> succ; + bool operator<(const Line& rhs) const { + if (rhs.b != LLONG_MIN) return m < rhs.m; + const Line* s = succ(); + if (!s) return 0; + ll x = rhs.m; + return b - s->b < (s->m - m) * x; + } }; + struct HullDynamic : public multiset<Line> { - bool bad(iterator y) { - auto z = next(y); - if (y == begin()) { - if (z == end()) return 0; - return y->m == z->m && y->b <= z->b; - } - auto x = prev(y); - if (z == end()) return y->m == x->m && y->b <= x->b; - return (x->b - y->b)*(z->m - y->m) >= (y->b - z->b)*(y->m - x->m); - } - void insert_line(ll m, ll b) { // Laufzeit: O(log(n)) - auto y = insert({ m, b }); - y->succ = [=] { return next(y) == end() ? 0 : &*next(y); }; - if (bad(y)) { erase(y); return; } - while (next(y) != end() && bad(next(y))) erase(next(y)); - while (y != begin() && bad(prev(y))) erase(prev(y)); - } - ll query(ll x) { // Laufzeit: O(log(n)) - auto l = *lower_bound((Line) {x, LLONG_MIN}); - return l.m * x + l.b; - } + bool bad(iterator y) { + auto z = next(y); + if (y == begin()) { + if (z == end()) return 0; + return y->m == z->m && y->b <= z->b; + } + auto x = prev(y); + if (z == end()) return y->m == x->m && y->b <= x->b; + return (x->b - y->b)*(z->m - y->m) >= + (y->b - z->b)*(y->m - x->m); + } + + // Variant 1: niklasb (2015) + void insert_line(ll m, ll b) { // Laufzeit: O(log(n)) + auto y = insert({m, b}); + y->succ = [=] {return next(y) == end() ? 0 : &*next(y);}; + if (bad(y)) {erase(y); return;} + while (next(y) != end() && bad(next(y))) erase(next(y)); + while (y != begin() && bad(prev(y))) erase(prev(y)); + } + + // Variant 2: barni120400 (2019) + void insert_line(ll m, ll b) { + auto y = insert({ m, b }); + if (bad(y)) {erase(y); return;} + while (next(y) != end() && bad(next(y))) erase(next(y)); + y->succ = [=] {return next(y) == end() ? 0 : &*next(y);}; + while (y != begin() && bad(prev(y))) erase(prev(y)); + if (y != begin()) prev(y)->succ = [=] {return &*y;}; + } + + ll query(ll x) { // Laufzeit: O(log(n)) + auto l = *lower_bound((Line) {x, MIN}); + return l.m * x + l.b; + } }; diff --git a/datastructures/fenwickTree.cpp b/datastructures/fenwickTree.cpp index 86b1138..cac3cf8 100644 --- a/datastructures/fenwickTree.cpp +++ b/datastructures/fenwickTree.cpp @@ -1,21 +1,15 @@ -vector<int> FT; // Fenwick-Tree -int n; +vector<ll> tree; -// Addiert val zum Element an Index i. O(log(n)). -void updateFT(int i, int val) { - i++; while(i <= n) { FT[i] += val; i += (i & (-i)); } +void update(int i, ll val) { + for (i++; i < sz(tree); i += (i & (-i))) tree[i] += val; } -// Baut Baum auf. O(n*log(n)). -void buildFenwickTree(vector<int>& a) { - n = a.size(); - FT.assign(n+1,0); - for(int i = 0; i < n; i++) updateFT(i,a[i]); +void init(int n) { + tree.assign(n + 1,0); } -// Präfix-Summe über das Intervall [0..i]. O(log(n)). -int prefix_sum(int i) { - int sum = 0; i++; - while(i > 0) { sum += FT[i]; i -= (i & (-i)); } - return sum; +ll prefix_sum(int i) { + ll sum = 0; + for (i++; i > 0; i -= (i & (-i))) sum += tree[i]; + return sum; } diff --git a/datastructures/fenwickTree2.cpp b/datastructures/fenwickTree2.cpp new file mode 100644 index 0000000..dfd5dc5 --- /dev/null +++ b/datastructures/fenwickTree2.cpp @@ -0,0 +1,21 @@ +vector<ll> add, mul; + +void update(int l, int r, ll val) { + for (int tl = l + 1; tl < sz(add); tl += tl&(-tl)) + add[tl] += val, mul[tl] -= val * l; + for (int tr = r + 1; tr < sz(add); tr += tr&(-tr)) + add[tr] -= val, mul[tr] += val * r; +} + +void init(vector<ll>& v) { + mul.assign(sz(v) + 1,0); + add.assign(sz(v) + 1,0); + for(int i = 0; i < sz(v); i++) update(i, i + 1, v[i]); +} + +ll prefix_sum (int i) { + ll res = 0; i++; + for (int ti = i; ti > 0; ti -= ti&(-ti)) + res += add[ti] * i + mul[ti]; + return res; +} diff --git a/datastructures/fenwickTreeNiklas.cpp b/datastructures/fenwickTreeNiklas.cpp deleted file mode 100644 index 032f74c..0000000 --- a/datastructures/fenwickTreeNiklas.cpp +++ /dev/null @@ -1,56 +0,0 @@ -const int n = 10000; // ALL INDICES START AT 1 WITH THIS CODE!! - -// mode 1: update indices, read prefixes -void update_idx(int tree[], int i, int val) { // v[i] += val - for (; i <= n; i += i & -i) tree[i] += val; -} -int read_prefix(int tree[], int i) { // get sum v[1..i] - int sum = 0; - for (; i > 0; i -= i & -i) sum += tree[i]; - return sum; -} -int kth(int k) { // find kth element in tree (1-based index) - int ans = 0; - for (int i = maxl; i >= 0; --i) // maxl = largest i s.t. (1<<i) <= n - if (ans + (1<<i) <= n && tree[ans + (1<<i)] < k) { - ans += 1<<i; - k -= tree[ans]; - } - return ans+1; -} - -// mode 2: update prefixes, read indices -void update_prefix(int tree[], int i, int val) { // v[1..i] += val - for (; i > 0; i -= i & -i) tree[i] += val; -} -int read_idx(int tree[], int i) { // get v[i] - int sum = 0; - for (; i <= n; i += i & -i) sum += tree[i]; - return sum; -} - -// mode 3: range-update range-query -const int maxn = 100100; -int n; -ll mul[maxn], add[maxn]; - -void update_idx(ll tree[], int x, ll val) { - for (int i = x; i <= n; i += i & -i) tree[i] += val; -} -void update_prefix(int x, ll val) { // v[x] += val - update_idx(mul, 1, val); - update_idx(mul, x + 1, -val); - update_idx(add, x + 1, x * val); -} -ll read_prefix(int x) { // get sum v[1..x] - ll a = 0, b = 0; - for (int i = x; i > 0; i -= i & -i) a += mul[i], b += add[i]; - return a * x + b; -} -void update_range(int l, int r, ll val) { // v[l..r] += val - update_prefix(l - 1, -val); - update_prefix(r, val); -} -ll read_range(int l, int r) { // get sum v[l..r] - return read_prefix(r) - read_prefix(l - 1); -} diff --git a/datastructures/firstUnused.cpp b/datastructures/firstUnused.cpp new file mode 100644 index 0000000..16b0c21 --- /dev/null +++ b/datastructures/firstUnused.cpp @@ -0,0 +1,13 @@ +// Erste natürliche Zahl nicht im set used. +set<int> used; +int unusedCounter = 1; + +int get_first_unused() { // Laufzeit: O(log n) amortisiert. + auto it = used.lower_bound(unusedCounter); + while (it != used.end() && *it == unusedCounter) { + it++; + unusedCounter++; + } + used.insert(unusedCounter); + return unusedCounter++; +} diff --git a/datastructures/lazyPropagation1.cpp b/datastructures/lazyPropagation1.cpp new file mode 100644 index 0000000..9fdffb1 --- /dev/null +++ b/datastructures/lazyPropagation1.cpp @@ -0,0 +1,47 @@ +int h; // = __lg(2*n) +//a value that is not used by the update query: +constexpr ll updateFlag = 0; +vector<ll> d(N, updateFlag); + +void apply(int p, ll value) { + tree[p] += value; + if (p < tree.size()/2) d[p] += value; +} + +void build(int p) { + while (p > 1) { + p/=2; + tree[p] = max(tree[2*p], tree[2*p+1]) + d[p]; +}} + +void push(int p) { + for (int s = h; s > 0; --s) { + int i = p >> s; + if (d[i] != updateFlag) { + apply(2*i , d[i]); + apply(2*i+1, d[i]); + d[i] = updateFlag; +}}} + +void inc(int l, int r, ll value) { + l += sz(tree)/2, r += sz(tree)/2; + int l0 = l, r0 = r; + for (; l < r; l/=2, r/=2) { + if (l&1) apply(l++, value); + if (r&1) apply(--r, value); + } + build(l0); build(r0 - 1); +} + +ll query(int l, int r) { + l += sz(tree)/2, r += sz(tree)/2; + push(l); + push(r - 1); + ll res = query_default; + for (; l < r; l/=2, r/=2) { + if (l&1) res = max(res, tree[l++]); + if (r&1) res = max(tree[--r], res); + } + return res; +} + diff --git a/datastructures/lazyPropagation2.cpp b/datastructures/lazyPropagation2.cpp new file mode 100644 index 0000000..e90c671 --- /dev/null +++ b/datastructures/lazyPropagation2.cpp @@ -0,0 +1,50 @@ +void calc(int p, ll k) { + if (d[p] == updateFlag) tree[p] = tree[2*p] + tree[2*p+1]; + else tree[p] = d[p] * k; +} + +void apply(int p, ll value, int k) { + tree[p] = value * k; + if (p < sz(tree)/2) d[p] = value; +} + +void build(int l, int r) { + int k = 2; + for (l += sz(tree)/2, r += sz(tree)/2-1; l > 1; k <<= 1) { + l/=2, r/=2; + for (int i = r; i >= l; --i) calc(i, k); +}} + +void push(int l, int r) { + int s = h, k = 1 << (h-1); + for (l += sz(tree)/2, r += sz(tree)/2-1; s > 0; --s, k/=2) { + for (int i = l >> s; i <= r >> s; ++i) { + if (d[i] != updateFlag) { + apply(2*i , d[i], k); + apply(2*i+1, d[i], k); + d[i] = updateFlag; +}}}} + +void modify(int l, int r, ll value) { + if (value == updateFlag) return; + push(l, l + 1); + push(r - 1, r); + int l0 = l, r0 = r, k = 1; + for (l += sz(tree)/2, r += sz(tree)/2; l<r; l/=2, r/=2, k*=2) { + if (l&1) apply(l++, value, k); + if (r&1) apply(--r, value, k); + } + build(l0, l0 + 1); + build(r0 - 1, r0); +} + +ll query(int l, int r) { + push(l, l + 1); + push(r - 1, r); + ll res = query_default; + for (l += sz(tree)/2, r += sz(tree)/2; l < r; l/=2, r/=2) { + if (l&1) res += tree[l++]; + if (r&1) res += tree[--r]; + } + return res; +} diff --git a/datastructures/monotonicConvexHull.cpp b/datastructures/monotonicConvexHull.cpp index 64af841..8035b17 100644 --- a/datastructures/monotonicConvexHull.cpp +++ b/datastructures/monotonicConvexHull.cpp @@ -3,21 +3,22 @@ vector<ll> ms, bs; int ptr = 0; bool bad(int l1, int l2, int l3) { - return (bs[l3]-bs[l1])*(ms[l1]-ms[l2]) < (bs[l2]-bs[l1])*(ms[l1]-ms[l3]); + return (bs[l3]-bs[l1])*(ms[l1]-ms[l2]) < + (bs[l2]-bs[l1])*(ms[l1]-ms[l3]); } void add(ll m, ll b) { // Laufzeit O(1) amortisiert - ms.push_back(m); bs.push_back(b); - while (ms.size() >= 3 && bad(ms.size() - 3, ms.size() - 2, ms.size() - 1)) { - ms.erase(ms.end() - 2); bs.erase(bs.end() - 2); - } - ptr = min(ptr, ms.size() - 1); + ms.push_back(m); bs.push_back(b); + while (sz(ms) >= 3 && bad(sz(ms)-3, sz(ms)-2, sz(ms)-1)) { + ms.erase(ms.end() - 2); bs.erase(bs.end() - 2); + } + ptr = min(ptr, sz(ms) - 1); } -ll get(int idx, ll x) { return ms[idx] * x + bs[idx]; } +ll get(int idx, ll x) {return ms[idx] * x + bs[idx];} ll query(ll x) { // Laufzeit: O(1) amortisiert - if (ptr >= (int)ms.size()) ptr = ms.size() - 1; - while (ptr < (int)ms.size() - 1 && get(ptr + 1, x) < get(ptr, x)) ptr++; - return ms[ptr] * x + bs[ptr]; + if (ptr >= sz(ms)) ptr = sz(ms) - 1; + while (ptr < sz(ms)-1 && get(ptr + 1, x) < get(ptr, x)) ptr++; + return get(ptr, x); } diff --git a/datastructures/persistent.cpp b/datastructures/persistent.cpp new file mode 100644 index 0000000..befecf7 --- /dev/null +++ b/datastructures/persistent.cpp @@ -0,0 +1,19 @@ +template<typename T>
+struct persistent {
+ int& time;
+ vector<pair<int, T>> data;
+
+ persistent(int& time, T value = {})
+ : time(time), data(1, {time, value}) {}
+
+ T get(int t) {
+ return prev(upper_bound(all(data),
+ pair<int, T>(t+1, {})))->second;
+ }
+
+ int set(T value) {
+ time+=2;
+ data.push_back({time, value});
+ return time;
+ }
+};
diff --git a/datastructures/persistentArray.cpp b/datastructures/persistentArray.cpp new file mode 100644 index 0000000..665d908 --- /dev/null +++ b/datastructures/persistentArray.cpp @@ -0,0 +1,26 @@ +template<typename T>
+struct persistentArray{
+ int time = 0;
+ vector<persistent<T>> data;
+ vector<pair<int, int>> mods;
+
+ persistentArray(int n, T value = {})
+ : time(0), data(n, {time, value}) {}
+
+ T get(int p, int t) {
+ return data[p].get(t);
+ }
+
+ int set(int p, T value) {
+ mods.push_back({p, time});
+ return data[p].set(value);
+ }
+
+ void reset(int t) {
+ while (!mods.empty() && mods.back().second > t) {
+ data[mods.back().first].data.pop_back();
+ mods.pop_back();
+ }
+ time = t;
+ }
+};
\ No newline at end of file diff --git a/datastructures/segmentTree.cpp b/datastructures/segmentTree.cpp index 3905151..09ff694 100644 --- a/datastructures/segmentTree.cpp +++ b/datastructures/segmentTree.cpp @@ -1,27 +1,43 @@ -// Laufzeit: init: O(n), query: O(log n), update: O(log n) -// Berechnet das Maximum im Array. -int a[MAX_N], m[4 * MAX_N]; +vector<ll> tree; +constexpr ll query_default = 0; -int query(int x, int y, int k = 0, int X = 0, int Y = MAX_N - 1) { - if (x <= X && Y <= y) return m[k]; - if (y < X || Y < x) return -INF; // Ein "neutrales" Element. - int M = (X + Y) / 2; - return max(query(x, y, 2*k+1, X, M), query(x, y, 2*k+2, M+1, Y)); +ll combine(ll a, ll b) { + return a + b; } -void update(int i, int v, int k = 0, int X = 0, int Y = MAX_N - 1) { - if (i < X || Y < i) return; - if (X == Y) { m[k] = v; a[i] = v; return; } - int M = (X + Y) / 2; - update(i, v, 2 * k + 1, X, M); - update(i, v, 2 * k + 2, M + 1, Y); - m[k] = max(m[2 * k + 1], m[2 * k + 2]); +void init(vector<ll>& values) { + tree.assign(sz(values)*2, 0); + copy(values.begin(), values.end(), tree.begin() + sz(values)); + for (int i = sz(tree)/2 - 1; i > 0; --i) { + tree[i] = combine(tree[2*i], tree[2*i+1]); +}} + +void update(int p, ll value) { + for (tree[p += sz(tree)/2] = value, p /= 2; p > 0; p /= 2) { + tree[p] = combine(tree[2*p], tree[2*p+1]); +}} + +ll query(int l, int r) { + ll resL = query_default; + ll resR = query_default; + for (l += sz(tree)/2, r += sz(tree)/2; l < r; l /= 2, r /= 2) { + if (l&1) resL = combine(resL, tree[l++]); + if (r&1) resR = combine(tree[--r], resR); + } + return combine(resL, resR); } -void init(int k = 0, int X = 0, int Y = MAX_N - 1) { - if (X == Y) { m[k] = a[X]; return; } - int M = (X + Y) / 2; - init(2 * k + 1, X, M); - init(2 * k + 2, M + 1, Y); - m[k] = max(m[2 * k + 1], m[2 * k + 2]); +// Oder: Intervall-Modifikation, Punkt-Query: +void modify(int l, int r, ll value) { + for (l += sz(tree)/2, r += sz(tree)/2; l < r; l /= 2, r /= 2) { + if (l&1) {tree[l] = combine(tree[l], value); l++;}; + if (r&1) {--r; tree[r] = combine(value, tree[r]);}; +}} + +ll query(int p) { + ll res = query_default; + for (p += sz(tree)/2; p > 0; p = p/2) { + res = combine(res, tree[p]); + } + return res; } diff --git a/datastructures/segmentTree2D.cpp b/datastructures/segmentTree2D.cpp deleted file mode 100644 index a9d129b..0000000 --- a/datastructures/segmentTree2D.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// 1-indiziert. Array t: [4*n][4*m]. Nur die _x-Varianten aufrufen. -// Laufzeit: build: O(n*m), update, sum: O(log(n)*log(m)) -void build_y(int vx, int lx, int rx, int vy, int ly, int ry) { - if (ly == ry) { - if (lx == rx)vt[vx][vy] = a[lx][ly]; - else t[vx][vy] = t[vx*2][vy] + t[vx*2+1][vy]; - } else { - int my = (ly + ry) / 2; - build_y(vx, lx, rx, vy*2, ly, my); - build_y(vx, lx, rx, vy*2+1, my+1, ry); - t[vx][vy] = t[vx][vy*2] + t[vx][vy*2+1]; -}} - -void build_x(int vx = 1, int lx = 0, int rx = N-1) { - if (lx != rx) { - int mx = (lx + rx) / 2; - build_x(vx*2, lx, mx); - build_x(vx*2+1, mx+1, rx); - } - build_y(vx, lx, rx, 1, 0, m-1); -} - -int sum_y(int vx, int vy, int tly, int try_, int ly, int ry) { - if (ly > ry) return 0; - if (ly == tly && try_ == ry) return t[vx][vy]; - int tmy = (tly + try_) / 2; - return sum_y(vx, vy*2, tly, tmy, ly, min(ry,tmy)) - + sum_y(vx, vy*2+1, tmy+1, try_, max(ly,tmy+1), ry); -} - -int sum_x(int vx=1, int tlx=0, int trx=n-1,int lx,int rx,int ly,int ry) { - if (lx > rx) return 0; - if (lx == tlx && trx == rx) return sum_y(vx, 1, 0, m-1, ly, ry); - int tmx = (tlx + trx) / 2; - return sum_x(vx*2, tlx, tmx, lx, min(rx,tmx), ly, ry) - + sum_x(vx*2+1, tmx+1, trx, max(lx,tmx+1), rx, ly, ry); -} - -void update_y(int vx, int lx, int rx, int vy, int ly, int ry, - int x, int y, int new_val) { - if (ly == ry) { - if (lx == rx) t[vx][vy] = new_val; - else t[vx][vy] = t[vx*2][vy] + t[vx*2+1][vy]; - } else { - int my = (ly + ry) / 2; - if (y <= my) update_y(vx, lx, rx, vy*2, ly, my, x, y, new_val); - else update_y(vx, lx, rx, vy*2+1, my+1, ry, x, y, new_val); - t[vx][vy] = t[vx][vy*2] + t[vx][vy*2+1]; -}} - -void update_x(int vx=1, int lx=0, int rx=n-1, int x, int y, int new_val) { - if (lx != rx) { - int mx = (lx + rx) / 2; - if (x <= mx) update_x(vx*2, lx, mx, x, y, new_val); - else update_x(vx*2+1, mx+1, rx, x, y, new_val); - } - update_y(vx, lx, rx, 1, 0, m-1, x, y, new_val); -} diff --git a/datastructures/sparseTable.cpp b/datastructures/sparseTable.cpp index 52867de..3d11119 100644 --- a/datastructures/sparseTable.cpp +++ b/datastructures/sparseTable.cpp @@ -1,27 +1,24 @@ struct SparseTable { - int st[MAX_N][MAX_LOG + 1], log[MAX_N + 1]; // Achtung: 2^MAX_LOG > MAX_N - vector<int> *a; + vector<vector<int>> st; + vector<ll> *a; - // Funktion muss idempotent sein! Hier Minimum. - bool better(int lidx, int ridx) { return a->at(lidx) <= a->at(ridx); } + bool better(int lidx, int ridx) { + return a->at(lidx) <= a->at(ridx); + } - void init(vector<int> *vec) { + void init(vector<ll> *vec) { a = vec; - for (int i = 0; i < (int)a->size(); i++) st[i][0] = i; - for (int j = 1; j <= MAX_LOG; j++) { - for (int i = 0; i + (1 << j) <= (int)a->size(); i++) { - st[i][j] = better(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]) - ? st[i][j - 1] : st[i + (1 << (j - 1))][j - 1]; - }} + st.assign(__lg(sz(*a)) + 1, vector<int>(sz(*a))); + for (int i = 0; i < sz(*a); i++) st[0][i] = i; + for (int j = 0; (2 << j) <= sz(*a); j++) { + for (int i = 0; i + (2 << j) <= sz(*a); i++) { + st[j + 1][i] = better(st[j][i] , st[j][i + (1 << j)]) + ? st[j][i] : st[j][i + (1 << j)]; + }}} - log[1] = 0; - for (int i = 2; i <= MAX_N; i++) log[i] = log[i/2] + 1; - } - - // Gibt Index des Ergebnisses in [l,r]. Laufzeit: O(1) int queryIdempotent(int l, int r) { - int j = log[r - l + 1]; - return better(st[l][j], st[r - (1 << j) + 1][j]) - ? st[l][j] : st[r - (1 << j) + 1][j]; + int j = __lg(r - l); //31 - __builtin_clz(r - l); + return better(st[j][l] , st[j][r - (1 << j)]) + ? st[j][l] : st[j][r - (1 << j)]; } -}; +};
\ No newline at end of file diff --git a/datastructures/stlHashMap.cpp b/datastructures/stlHashMap.cpp new file mode 100644 index 0000000..b107dde --- /dev/null +++ b/datastructures/stlHashMap.cpp @@ -0,0 +1,17 @@ +#include <ext/pb_ds/assoc_container.hpp> +using namespace __gnu_pbds; + +template<typename T> +struct betterHash { + size_t operator()(T o) const { + size_t h = hash<T>()(o) ^ 42394245; //random value + h = ((h >> 16) ^ h) * 0x45d9f3b; + h = ((h >> 16) ^ h) * 0x45d9f3b; + h = ((h >> 16) ^ h); + return h; +}}; + +template<typename K, typename V, typename H = betterHash<K>> +using hashMap = gp_hash_table<K, V, H>; +template<typename K, typename H = betterHash<K>> +using hashSet = gp_hash_table<K, null_type, H>; diff --git a/datastructures/stlPQ.cpp b/datastructures/stlPQ.cpp index 1de4abf..b48223b 100644 --- a/datastructures/stlPQ.cpp +++ b/datastructures/stlPQ.cpp @@ -1,6 +1,7 @@ #include <ext/pb_ds/priority_queue.hpp> template<typename T> -using priorityQueue = __gnu_pbds::priority_queue<T, less<T>>; // greater<T> für Min-Queue +// greater<T> für Min-Queue +using priorityQueue = __gnu_pbds::priority_queue<T, less<T>>; int main() { priorityQueue<int> pq; diff --git a/datastructures/stlRope.cpp b/datastructures/stlRope.cpp index 9179a60..804cd67 100644 --- a/datastructures/stlRope.cpp +++ b/datastructures/stlRope.cpp @@ -5,4 +5,4 @@ v.push_back(num); // O(log(n)) rope<int> sub = v.substr(start, length); // O(log(n)) v.erase(start, length); // O(log(n)) v.insert(v.mutable_begin() + offset, sub); // O(log(n)) -for(auto it = v.mutable_begin(); it != v.mutable_end(); it++) {...} +for(auto it = v.mutable_begin(); it != v.mutable_end(); it++) diff --git a/datastructures/stlTree.cpp b/datastructures/stlTree.cpp index 6dde73a..29491c4 100644 --- a/datastructures/stlTree.cpp +++ b/datastructures/stlTree.cpp @@ -1,14 +1,16 @@ -#include <bits/stdc++.h> #include <ext/pb_ds/assoc_container.hpp> #include <ext/pb_ds/tree_policy.hpp> using namespace std; using namespace __gnu_pbds; -typedef tree<int, null_type, less<int>, rb_tree_tag, - tree_order_statistics_node_update> Tree; +template<typename T> +using Tree = tree<T, null_type, less<T>, rb_tree_tag, + tree_order_statistics_node_update>; int main() { - Tree X; - for (int i = 1; i <= 16; i <<= 1) X.insert(i); // {1, 2, 4, 8, 16} + Tree<int> X; + // insert {1, 2, 4, 8, 16} + for (int i = 1; i <= 16; i *= 2) X.insert(i); cout << *X.find_by_order(3) << endl; // => 8 - cout << X.order_of_key(10) << endl; // => 4 = min i, mit X[i] >= 10 + cout << X.order_of_key(10) << endl; + // => 4 = min i, mit X[i] >= 10 return 0; } diff --git a/datastructures/treap.cpp b/datastructures/treap.cpp index 4de3328..6296392 100644 --- a/datastructures/treap.cpp +++ b/datastructures/treap.cpp @@ -1,39 +1,79 @@ -struct item { - int key, prior; - item *l, *r; - item() {} - item(int key, int prior) : key(key), prior(prior), l(NULL), r(NULL) {} +struct node { + int key, prio, left, right, size; + node(int key, int prio) : key(key), prio(prio), left(-1), + right(-1), size(1) {}; }; -void split(item *t, int key, item *l, item *r) { - if (!t) l = r = NULL; - else if (key < t->key) split(t->l, key, l, t->l), r = t; - else split(t->r, key, t->r, r), l = t; -} +vector<node> treap; -void insert(item *t, item *it) { - if (!t) t = it; - else if (it->prior > t->prior) split(t, it->key, it->l, it->r), t = it; - else insert(it->key < t->key ? t->l : t->r, it); +int getSize(int root) { + return root < 0 ? 0 : treap[root].size; } -void merge(item *t, item *l, item *r) { - if (!l || !r) t = l ? l : r; - else if (l->prior > r->prior) merge(l->r, l->r, r), t = l; - else merge(r->l, l, r->l), t = r; +void update(int root) { + if (root < 0) return; + treap[root].size = 1 + getSize(treap[root].left) + + getSize(treap[root].right); } -void erase(item *t, int key) { - if (t->key == key) merge (t, t->l, t->r); - else erase(key < t->key ? t->l : t->r, key); +pair<int, int> split(int root, int minKeyRight) { + if (root < 0) return {-1, -1}; + if (treap[root].key >= minKeyRight) { + auto leftSplit = split(treap[root].left, minKeyRight); + treap[root].left = leftSplit.second; + update(root); + leftSplit.second = root; + return leftSplit; + } else { + auto rightSplit = split(treap[root].right, minKeyRight); + treap[root].right = rightSplit.first; + update(root); + rightSplit.first = root; + return rightSplit; +}} + +int merge (int left, int right) { + if (left < 0) return right; + if (right < 0) return left; + if (treap[left].prio < treap[right].prio) { //min priority heap + treap[left].right = merge(treap[left].right, right); + update(left); + return left; + } else { + treap[right].left = merge(left, treap[right].left); + update(right); + return right; +}} + +//insert values with high priority first +int insert(int root, int key, int prio) { + int next = sz(treap); + treap.emplace_back(key, prio); + auto t = split(root, key); + //returns new root + return merge(merge(t.first, next), t.second); } -item *unite(item *l, item *r) { - if (!l || !r) return l ? l : r; - if (l->prior < r->prior) swap(l, r); - item * lt, rt; - split(r, l->key, lt, rt); - l->l = unite(l->l, lt); - l->r = unite(l->r, rt); - return l; +int remove(int root, int key) { + if (root < 0) return -1; + if (key < treap[root].key) { + treap[root].left = remove(treap[root].left, key); + update(root); + return root; + } else if (key > treap[root].key) { + treap[root].right = remove(treap[root].right, key); + update(root); + return root; + } else { //check prio? + return merge(treap[root].left, treap[root].right); +}} + +int kth(int root, int k) { + if (root < 0) return -1; + int leftSize = getSize(treap[root].left); + if (k < leftSize) return kth(treap[root].left, k); + else if (k > leftSize) { + return kth(treap[root].right, k - 1 - leftSize); + } + return root; } diff --git a/datastructures/unionFind.cpp b/datastructures/unionFind.cpp index 99b19fc..03ff63e 100644 --- a/datastructures/unionFind.cpp +++ b/datastructures/unionFind.cpp @@ -1,21 +1,22 @@ -// Laufzeit: O(n*alpha(n)) -// "height" ist obere Schranke für die Höhe der Bäume. Sobald -// Pfadkompression angewendet wurde, ist die genaue Höhe nicht mehr -// effizient berechenbar. -vector<int> parent; // Initialisiere mit Index im Array. -vector<int> height; // Initialisiere mit 0. +// unions[i] >= 0 => unions[i] = parent +// unions[i] < 0 => unions[i] = -height +vector<int> unions; + +void init(int n) { //Initialisieren + unions.assign(n, -1); +} int findSet(int n) { // Pfadkompression - if (parent[n] != n) parent[n] = findSet(parent[n]); - return parent[n]; + if (unions[n] < 0) return n; + return unions[n] = findSet(unions[n]); } void linkSets(int a, int b) { // Union by rank. - if (height[a] < height[b]) parent[a] = b; - else if (height[b] < height[a]) parent[b] = a; + if (unions[a] > unions[b]) unions[a] = b; + else if (unions[b] > unions[a]) unions[b] = a; else { - parent[a] = b; - height[b]++; + unions[a] = b; + unions[b]--; }} void unionSets(int a, int b) { // Diese Funktion aufrufen. diff --git a/datastructures/unionFind2.cpp b/datastructures/unionFind2.cpp new file mode 100644 index 0000000..225ecee --- /dev/null +++ b/datastructures/unionFind2.cpp @@ -0,0 +1,27 @@ +vector<int> uf; + +init(int N) { + uf.assign(N,-1); //-1 indicates that every subset has size 1 +} + +int findSet(int i) { + if(uf[i] < 0) return i; //If uf[i] < 0 we have reach a root + uf[i] = findSet(uf[i]); //Path-Compression + return uf[i]; +} + +void linkSets(int i, int j) { + //Take |uf[i]|, where i must be a root, to get the size + //of the subset + if(abs(uf[i]) < abs(uf[j])) { //Union-by-size. + uf[j] += uf[i]; uf[i] = j; + } else { + uf[i] += uf[j]; uf[j] = i; + } +} + +void unionSets(int i, int j) { + if(findSet(i) != findSet(j)) linkSets(findSet(i),findSet(j)); +} + + diff --git a/datastructures/waveletTree.cpp b/datastructures/waveletTree.cpp new file mode 100644 index 0000000..feef2d4 --- /dev/null +++ b/datastructures/waveletTree.cpp @@ -0,0 +1,48 @@ +struct WaveletTree {
+ using it = vector<ll>::iterator;
+ WaveletTree *ln, *rn;
+ ll lo, hi;
+ vector<int> b;
+
+private:
+ WaveletTree(it from, it to, ll x, ll y)
+ : ln(nullptr), rn(nullptr), lo(x), hi(y), b(1) {
+ ll mid = (lo + hi) / 2;
+ auto f = [&](ll x){return x < mid;};
+ for (it c = from; c != to; c++) {
+ b.push_back(b.back() + f(*c));
+ }
+ if (lo + 1 >= hi || from == to) return;
+ it pivot = stable_partition(from, to, f);
+ ln = new WaveletTree(from, pivot, lo, mid);
+ rn = new WaveletTree(pivot, to, mid, hi);
+ }
+
+public:
+ WaveletTree(vector<ll> in) : WaveletTree(all(in),
+ *min_element(all(in)), *max_element(all(in)) + 1){}
+
+ // kth element in sort[l, r) all 0-indexed
+ ll kth(int l, int r, int k) {
+ if (l >= r || k >= r - l) return -1;
+ if (lo + 1 >= hi) return lo;
+ int inLeft = b[r] - b[l];
+ if (k < inLeft) {
+ return ln->kth(b[l], b[r], k);
+ } else {
+ return rn->kth(l-b[l], r-b[r], k-inLeft);
+ }}
+
+ // count elements in[l, r) smaller than k
+ int countSmaller(int l, int r, ll k) {
+ if (l >= r || k <= lo) return 0;
+ if (hi <= k) return r - l;
+ return ln->countSmaller(b[l], b[r], k) +
+ rn->countSmaller(l-b[l], r-b[r], k);
+ }
+
+ ~WaveletTree(){
+ delete ln;
+ delete rn;
+ }
+};
diff --git a/geometry/antipodalPoints.cpp b/geometry/antipodalPoints.cpp new file mode 100644 index 0000000..c1921cc --- /dev/null +++ b/geometry/antipodalPoints.cpp @@ -0,0 +1,13 @@ +vector<pair<int, int>> antipodalPoints(vector<pt>& h) { + vector<pair<int, int>> result; + int n = (int)h.size(); + if (n < 2) return result; + for (int i = 0, j = 1; i < j; i++) { + while (true) { + result.push_back({i, j}); + if (cross(h[(i + 1) % n] - h[i], + h[(j + 1) % n] - h[j]) <= 0) break; + j = (j + 1) % n; + }} + return result; +} diff --git a/geometry/circle.cpp b/geometry/circle.cpp new file mode 100644 index 0000000..f49ea27 --- /dev/null +++ b/geometry/circle.cpp @@ -0,0 +1,33 @@ +// berechnet die Schnittpunkte von zwei kreisen +// (Kreise dürfen nicht gleich sein!) +vector<pt> circleIntersection(pt c1, double r1, + pt c2, double r2) { + double d = abs(c1 - c2); + if (d < abs(r1 - r2) || d > abs(r1 + r2)) return {}; + double a = (r1 * r1 - r2 * r2 + d * d) / (2 * d); + pt p = (c2 - c1) * a / d + c1; + if (d == abs(r1 - r2) || d == abs(r1 + r2)) return {p}; + double h = sqrt(r1 * r1 - a * a); + return {p + pt{0, 1} * (c2 - c1) * h / d, + p - pt{0, 1} * (c2 - c1) * h / d}; +} + +// berechnet die Schnittpunkte zwischen +// einem Kreis(Kugel) und einer Grade 2d und 3d +vector<pt> circleRayIntersection(pt center, double r, + pt orig, pt dir) { + vector<pt> result; + double a = dot(dir, dir); + double b = 2 * dot(dir, orig - center); + double c = dot(orig - center, orig - center) - r * r; + double discr = b * b - 4 * a * c; + if (discr >= 0) { + //t in [0, 1] => schnitt mit segment [orig, orig + dir] + double t1 = -(b + sqrt(discr)) / (2 * a); + double t2 = -(b - sqrt(discr)) / (2 * a); + if (t1 >= 0) result.push_back(t1 * dir + orig); + if (t2 >= 0 && abs(t1 - t2) > EPS) { + result.push_back(t2 * dir + orig); + }} + return result; +} diff --git a/geometry/closestPair.cpp b/geometry/closestPair.cpp index cac544b..a7662b6 100644 --- a/geometry/closestPair.cpp +++ b/geometry/closestPair.cpp @@ -1,35 +1,40 @@ double squaredDist(pt a, pt b) { - return (a.fst-b.fst) * (a.fst-b.fst) + (a.snd-b.snd) * (a.snd-b.snd); + return real(conj(a-b) * (a-b)); } bool compY(pt a, pt b) { - if (a.snd == b.snd) return a.fst < b.fst; - return a.snd < b.snd; + return (imag(a) == imag(b)) ? real(a) < real(b) + : imag(a) < imag(b); } -// points.size() > 1 und alle Punkte müssen verschieden sein! -double shortestDist(vector<pt> &points) { +bool compX(pt a, pt b) { + return (real(a) == real(b)) ? imag(a) < imag(b) + : real(a) < real(b); +} + +double shortestDist(vector<pt>& points) { // points.size() > 1 set<pt, bool(*)(pt, pt)> status(compY); - sort(points.begin(), points.end()); - double opt = 1e30, sqrtOpt = 1e15; + sort(points.begin(), points.end(), compX); + double opt = 1.0/0.0, sqrtOpt = 1.0/0.0; auto left = points.begin(), right = points.begin(); status.insert(*right); right++; - + while (right != points.end()) { - if (fabs(left->fst - right->fst) >= sqrtOpt) { - status.erase(*(left++)); + if (left != right && + abs(real(*left - *right)) >= sqrtOpt) { + status.erase(*left); + left++; } else { - auto lower = status.lower_bound(pt(-1e20, right->snd - sqrtOpt)); - auto upper = status.upper_bound(pt(-1e20, right->snd + sqrtOpt)); - while (lower != upper) { + auto lower = status.lower_bound({-1.0/0.0, imag(*right) - sqrtOpt}); + auto upper = status.upper_bound({-1.0/0.0, imag(*right) + sqrtOpt}); + for (;lower != upper; lower++) { double cand = squaredDist(*right, *lower); if (cand < opt) { opt = cand; sqrtOpt = sqrt(opt); - } - ++lower; - } - status.insert(*(right++)); + }} + status.insert(*right); + right++; }} return sqrtOpt; } diff --git a/geometry/convexHull.cpp b/geometry/convexHull.cpp index 9c14c45..e988977 100644 --- a/geometry/convexHull.cpp +++ b/geometry/convexHull.cpp @@ -1,24 +1,19 @@ -// Laufzeit: O(n*log(n)) -ll cross(const pt p, const pt a, const pt b) { - return (a.x - p.x) * (b.y - p.y) - (a.y - p.y) * (b.x - p.x); -} - -// Punkte auf der konvexen Hülle, gegen den Uhrzeigersinn sortiert. -// Kollineare Punkte nicht enthalten, entferne dafür "=" im CCW-Test. -// Achtung: Der erste und letzte Punkt im Ergebnis sind gleich. -// Achtung: Alle Punkte müssen verschieden sein. vector<pt> convexHull(vector<pt> p){ - int n = p.size(), k = 0; - vector<pt> h(2 * n); - sort(p.begin(), p.end()); - for (int i = 0; i < n; i++) { // Untere Hülle. - while (k >= 2 && cross(h[k - 2], h[k - 1], p[i]) <= 0.0) k--; - h[k++] = p[i]; - } - for (int i = n - 2, t = k + 1; i >= 0; i--) { // Obere Hülle. - while (k >= t && cross(h[k - 2], h[k - 1], p[i]) <= 0.0) k--; - h[k++] = p[i]; - } - h.resize(k); - return h; + sort(p.begin(), p.end(), [](const pt& a, const pt& b){ + return real(a) == real(b) ? imag(a) < imag(b) + : real(a) < real(b); + }); + p.erase(unique(p.begin(), p.end()), p.end()); + int k = 0; + vector<pt> h(2 * sz(p)); + for (int i = 0; i < sz(p); i++) {// Untere Hülle. + while (k > 1 && cross(h[k-2], h[k-1], p[i]) <= 0) k--; + h[k++] = p[i]; + } + for (int i = sz(p)-2, t = k; i >= 0; i--) {// Obere Hülle. + while (k > t && cross(h[k-2], h[k-1], p[i]) <= 0) k--; + h[k++] = p[i]; + } + h.resize(k); + return h; } diff --git a/geometry/formulars.cpp b/geometry/formulars.cpp index ed44b8b..43affbb 100644 --- a/geometry/formulars.cpp +++ b/geometry/formulars.cpp @@ -1,165 +1,37 @@ -// Komplexe Zahlen als Darstellung für Punkte. Wenn immer möglich -// complex<int> verwenden. Funktionen wie abs() geben dann int zurück. -typedef complex<double> pt; +// Komplexe Zahlen als Darstellung für Punkte. +// Wenn immer möglich complex<int> verwenden. +// Funktionen wie abs() geben dann int zurück. +using pt = complex<double>; -// Winkel zwischen Punkt und x-Achse in [0, 2 * PI). -double angle = arg(a); +// PIL < PI < PIU +constexpr double PIU = acos(-1.0l); +constexpr double PIL = PIU-2e-19l; -// Punkt rotiert um Winkel theta. -pt a_rotated = a * exp(pt(0, theta)); +// Winkel zwischen Punkt und x-Achse in [-PI, PI]. +double angle(pt a) {return arg(a);} -// Mittelpunkt des Dreiecks abc. -pt centroid = (a + b + c) / 3.0; +// rotiert Punkt im Uhrzeigersinn um den Ursprung. +pt rotate(pt a, double theta) {return a * exp(pt(0.0, theta));} // Skalarprodukt. -double dot(pt a, pt b) { return real(conj(a) * b); } +double dot(pt a, pt b) {return real(conj(a) * b);} -// Kreuzprodukt, 0, falls kollinear. -double cross(pt a, pt b) { return imag(conj(a) * b); } - -// Flächeninhalt eines Dreicks bei bekannten Eckpunkten. -double areaOfTriangle(pt a, pt b, pt c) { - return abs(cross(b - a, c - a)) / 2.0; -} +// abs()^2.(pre c++20) +double norm(pt a) {return dot(a, a);} -// Flächeninhalt eines Dreiecks bei bekannten Seitenlängen. -double areaOfTriangle(double a, double b, double c) { - double s = (a + b + c) / 2; - return sqrt(s * (s-a) * (s-b) * (s-c)); -} - -// Sind die Dreiecke a1, b1, c1, and a2, b2, c2 ähnlich? -// Erste Zeile testet Ähnlichkeit mit gleicher Orientierung, -// zweite Zeile testet Ähnlichkeit mit unterschiedlicher Orientierung -bool similar (pt a1, pt b1, pt c1, pt a2, pt b2, pt c2) { - return ( - (b2-a2) * (c1-a1) == (b1-a1) * (c2-a2) || - (b2-a2) * (conj(c1)-conj(a1)) == (conj(b1)-conj(a1)) * (c2-a2) - ); -} +// Kreuzprodukt, 0, falls kollinear. +double cross(pt a, pt b) {return imag(conj(a) * b);} +double cross(pt p, pt a, pt b) {return cross(a - p, b - p);} -// -1 => gegen den Uhrzeigersinn, 0 => kolliniear, 1 => im Uhrzeigersinn. -// Einschränken der Rückgabe auf [-1,1] ist sicherer gegen Overflows. -double orientation(pt a, pt b, pt c) { +// -1 => gegen den Uhrzeigersinn +// 0 => kolliniear +// 1 => im Uhrzeigersinn. +int orientation(pt a, pt b, pt c) { double orien = cross(b - a, c - a); - if (abs(orien) < EPSILON) return 0; // Braucht großes EPSILON: ~1e-6 - return orien < 0 ? -1 : 1; -} - -// Test auf Streckenschnitt zwischen a-b und c-d. -bool lineSegmentIntersection(pt a, pt b, pt c, pt d) { - if (orientation(a, b, c) == 0 && orientation(a, b, d) == 0) { - double dist = abs(a - b); - return (abs(a - c) <= dist && abs(b - c) <= dist) || - (abs(a - d) <= dist && abs(b - d) <= dist); - } - return orientation(a, b, c) * orientation(a, b, d) <= 0 && - orientation(c, d, a) * orientation(c, d, b) <= 0; -} - -// Berechnet die Schnittpunkte der Strecken a-b und c-d. Enthält entweder -// keinen Punkt, den einzigen Schnittpunkt oder die Endpunkte der -// Schnittstrecke. operator<, min, max müssen noch geschrieben werden! -vector<pt> lineSegmentIntersection(pt a, pt b, pt c, pt d) { - vector<pt> result; - if (orientation(a, b, c) == 0 && orientation(a, b, d) == 0 && - orientation(c, d, a) == 0 && orientation(c, d, b) == 0) { - pt minAB = min(a, b), maxAB = max(a, b); - pt minCD = min(c, d), maxCD = max(c, d); - if (minAB < minCD && maxAB < minCD) return result; - if (minCD < minAB && maxCD < minAB) return result; - pt start = max(minAB, minCD), end = min(maxAB, maxCD); - result.push_back(start); - if (start != end) result.push_back(end); - return result; - } - double x1 = real(b) - real(a), y1 = imag(b) - imag(a); - double x2 = real(d) - real(c), y2 = imag(d) - imag(c); - double u1 = (-y1 * (real(a) - real(c)) + x1 * (imag(a) - imag(c))) / - (-x2 * y1 + x1 * y2); - double u2 = (x2 * (imag(a) - imag(c)) - y2 * (real(a) - real(c))) / - (-x2 * y1 + x1 * y2); - if (u1 >= 0 && u1 <= 1 && u2 >= 0 && u2 <= 1) { - double x = real(a) + u2 * x1, y = imag(a) + u2 * y1; - result.push_back(pt(x, y)); - } - return result; -} - -// Entfernung von Punkt p zur Gearden durch a-b. -double distToLine(pt a, pt b, pt p) { - return abs(cross(p - a, b - a)) / abs(b - a); -} - -// Liegt p auf der Geraden a-b? -bool pointOnLine(pt a, pt b, pt p) { - return orientation(a, b, p) == 0; -} - -// Liegt p auf der Strecke a-b? -bool pointOnLineSegment(pt a, pt b, pt p) { - if (orientation(a, b, p) != 0) return false; - return real(p) >= min(real(a), real(b)) && - real(p) <= max(real(a), real(b)) && - imag(p) >= min(imag(a), imag(b)) && - imag(p) <= max(imag(a), imag(b)); -} - -// Entfernung von Punkt p zur Strecke a-b. -double distToSegment(pt a, pt b, pt p) { - if (a == b) return abs(p - a); - double segLength = abs(a - b); - double u = ((real(p) - real(a)) * (real(b) - real(a)) + - (imag(p) - imag(a)) * (imag(b) - imag(a))) / - (segLength * segLength); - pt projection(real(a) + u * (real(b) - real(a)), - imag(a) + u * (imag(b) - imag(a))); - double projectionDist = abs(p - projection); - if (!pointOnLineSegment(a, b, projection)) projectionDist = 1e30; - return min(projectionDist, min(abs(p - a), abs(p - b))); -} - -// Kürzeste Entfernung zwischen den Strecken a-b und c-d. -double distBetweenSegments(pt a, pt b, pt c, pt d) { - if (lineSegmentIntersection(a, b, c, d)) return 0.0; - double result = distToSegment(a, b, c); - result = min(result, distToSegment(a, b, d)); - result = min(result, distToSegment(c, d, a)); - return min(result, distToSegment(c, d, b)); + return (orien > EPS) - (orien < -EPS); } // Liegt d in der gleichen Ebene wie a, b, und c? bool isCoplanar(pt a, pt b, pt c, pt d) { - return abs((b - a) * (c - a) * (d - a)) < EPSILON; -} - -// Berechnet den Flächeninhalt eines Polygons (nicht selbstschneidend). -// Punkte gegen den Uhrzeigersinn: positiv, sonst negativ. -double areaOfPolygon(vector<pt> &polygon) { // Jeder Eckpunkt nur einmal. - double res = 0; int n = polygon.size(); - for (int i = 0; i < n; i++) - res += real(polygon[i]) * imag(polygon[(i + 1) % n]) - - real(polygon[(i + 1) % n]) * imag(polygon[i]); - return 0.5 * res; -} - -// Schneiden sich (p1, p2) und (p3, p4) (gegenüberliegende Ecken). -bool rectIntersection(pt p1, pt p2, pt p3, pt p4) { - double minx12=min(real(p1), real(p2)), maxx12=max(real(p1), real(p2)); - double minx34=min(real(p3), real(p4)), maxx34=max(real(p3), real(p4)); - double miny12=min(imag(p1), imag(p2)), maxy12=max(imag(p1), imag(p2)); - double miny34=min(imag(p3), imag(p4)), maxy34=max(imag(p3), imag(p4)); - return (maxx12 >= minx34) && (maxx34 >= minx12) && - (maxy12 >= miny34) && (maxy34 >= miny12); -} - -// Testet, ob ein Punkt im Polygon liegt (beliebige Polygone). -bool pointInPolygon(pt p, vector<pt> &polygon) { // Punkte nur einmal. - pt rayEnd = p + pt(1, 1000000); - int counter = 0, n = polygon.size(); - for (int i = 0; i < n; i++) { - pt start = polygon[i], end = polygon[(i + 1) % n]; - if (lineSegmentIntersection(p, rayEnd, start, end)) counter++; - } - return counter & 1; + return abs((b - a) * (c - a) * (d - a)) < EPS; } diff --git a/geometry/formulars3d.cpp b/geometry/formulars3d.cpp new file mode 100644 index 0000000..f527d57 --- /dev/null +++ b/geometry/formulars3d.cpp @@ -0,0 +1,53 @@ +// Skalarprodukt +double operator|(pt3 a, pt3 b) { + return a.x * b.x + a.y*b.y + a.z*b.z; +} +double dot(pt3 a, pt3 b) {return a|b;} + +// Kreuzprodukt +pt3 operator*(pt3 a, pt3 b) {return {a.y*b.z - a.z*b.y, + a.z*b.x - a.x*b.z, + a.x*b.y - a.y*b.x};} +pt3 cross(pt3 a, pt3 b) {return a*b;} + +// Länge von a +double abs(pt3 a) {return sqrt(dot(a, a));} +double abs(pt3 a, pt3 b) {return abs(b - a);} + +// Mixedprodukt +double mixed(pt3 a, pt3 b, pt3 c) {return a*b|c;}; + +// orientierung von p zu der Ebene durch a, b, c +// -1 => gegen den Uhrzeigersinn, +// 0 => kolliniear, +// 1 => im Uhrzeigersinn. +int orientation(pt3 a, pt3 b, pt3 c, pt3 p) { + double orien = mixed(b - a, c - a, p - a); + return (orien > EPS) - (orien < -EPS); +} + +// Entfernung von Punkt p zur Ebene a,b,c. +double distToPlane(pt3 a, pt3 b, pt3 c, pt3 p) { + pt3 n = cross(b-a, c-a); + return (abs(dot(n, p)) - dot(n, a)) / abs(n); +} + +// Liegt p in der Ebene a,b,c? +bool pointOnPlane(pt3 a, pt3 b, pt3 c, pt3 p) { + return orientation(a, b, c, p) == 0; +} + +// Schnittpunkt von der Grade a-b und der Ebene c,d,e +// die Grade darf nicht parallel zu der Ebene sein! +pt3 linePlaneIntersection(pt3 a, pt3 b, pt3 c, pt3 d, pt3 e) { + pt3 n = cross(d-c, e-c); + pt3 d = b - a; + return a - d * (dot(n, a) - dot(n, c)) / dot(n, d); +} + +// Abstand zwischen der Grade a-b und c-d +double lineLineDist(pt3 a, pt3 b, pt3 c, pt3 d) { + pt3 n = cross(b - a, d - c); + if (abs(n) < EPS) return distToLine(a, b, c); + return abs(dot(a - c, n)) / abs(n); +}
\ No newline at end of file diff --git a/geometry/geometry.tex b/geometry/geometry.tex index de7b2f6..1201917 100644 --- a/geometry/geometry.tex +++ b/geometry/geometry.tex @@ -1,13 +1,47 @@ \section{Geometrie} -\subsection{Closest Pair} -\lstinputlisting{geometry/closestPair.cpp} +\begin{algorithm}{Closest Pair} + \begin{methods} + \method{shortestDist}{kürzester Abstand zwischen Punkten}{n\*\log(n)} + \end{methods} + \sourcecode{geometry/closestPair.cpp} +\end{algorithm} -\subsection{Geraden} -\lstinputlisting{geometry/lines.cpp} +\begin{algorithm}{Konvexe Hülle} + \begin{methods} + \method{convexHull}{berechnet Konvexehülle}{n\*\log(n)} + \end{methods} + \begin{itemize} + \item Konvexehülle gegen den Uhrzeigersinn Sortiert + \item nur Eckpunkte enthalten(für alle Punkte = im CCW Test entfernen) + \item Erster und Letzter Punkt sind identisch + \end{itemize} + \sourcecode{geometry/convexHull.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} + +\subsection{Formeln~~--~\texttt{std::complex}} +\sourcecode{geometry/formulars.cpp} +\sourcecode{geometry/linesAndSegments.cpp} +\sourcecode{geometry/triangle.cpp} +\sourcecode{geometry/polygon.cpp} +\sourcecode{geometry/circle.cpp} +\sourcecode{geometry/sortAround.cpp} -\subsection{Konvexe Hülle} -\lstinputlisting{geometry/convexHull.cpp} +\subsection{Formeln - 3D} +\sourcecode{geometry/formulars3d.cpp} -\subsection{Formeln - \lstinline{std::complex}} -\lstinputlisting{geometry/formulars.cpp} +\subsection{3D-Kugeln} +\sourcecode{geometry/spheres.cpp} + +\optional{ +\subsection{Geraden} +\sourcecode{geometry/lines.cpp} +} diff --git a/geometry/lines.cpp b/geometry/lines.cpp index 2594ab4..95536a4 100644 --- a/geometry/lines.cpp +++ b/geometry/lines.cpp @@ -1,32 +1,33 @@ -// Nicht complex<double> benutzen. Eigene struct schreiben. struct line { double a, b, c; // ax + by + c = 0; vertikale Line: b = 0, sonst: b = 1 + line(pt p, pt q) : a(-imag(q-p)), b(real(q-p)), c(cross({b, -a},p)) {} }; line pointsToLine(pt p1, pt p2) { line l; - if (fabs(p1.x - p2.x) < EPSILON) { - l.a = 1; l.b = 0.0; l.c = -p1.x; + if (abs(real(p1 - p2)) < EPS) { + l.a = 1; l.b = 0.0; l.c = -real(p1); } else { - l.a = -(double)(p1.y - p2.y) / (p1.x - p2.x); + l.a = -imag(p1 - p2) / real(p1 - p2); l.b = 1.0; - l.c = -(double)(l.a * p1.x) - p1.y; + l.c = -(l.a * real(p1)) - imag(p1); } return l; } -bool areParallel(line l1, line l2) { - return (fabs(l1.a - l2.a) < EPSILON) && (fabs(l1.b - l2.b) < EPSILON); +bool parallel(line l1, line l2) { + return (abs(l1.a - l2.a) < EPS) && (abs(l1.b - l2.b) < EPS); } -bool areSame(line l1, line l2) { - return areParallel(l1, l2) && (fabs(l1.c - l2.c) < EPSILON); +bool same(line l1, line l2) { + return parallel(l1, l2) && (abs(l1.c - l2.c) < EPS); } -bool areIntersect(line l1, line l2, pt &p) { - if (areParallel(l1, l2)) return false; - p.x = (l2.b * l1.c - l1.b * l2.c) / (l2.a * l1.b - l1.a * l2.b); - if (fabs(l1.b) > EPSILON) p.y = -(l1.a * p.x + l1.c); - else p.y = -(l2.a * p.x + l2.c); +bool intersect(line l1, line l2, pt& p) { + if (parallel(l1, l2)) return false; + double y, x = (l2.b * l1.c - l1.b * l2.c) / (l2.a * l1.b - l1.a * l2.b); + if (abs(l1.b) > EPS) y = -(l1.a * x + l1.c); + else y = -(l2.a * x + l2.c); + p = {x, y}; return true; } diff --git a/geometry/linesAndSegments.cpp b/geometry/linesAndSegments.cpp new file mode 100644 index 0000000..6c53cee --- /dev/null +++ b/geometry/linesAndSegments.cpp @@ -0,0 +1,90 @@ +// Test auf Streckenschnitt zwischen a-b und c-d. +bool lineSegmentIntersection(pt a, pt b, pt c, pt d) { + if (orientation(a, b, c) == 0 && orientation(a, b, d) == 0) + return pointOnLineSegment(a,b,c) || + pointOnLineSegment(a,b,d) || + pointOnLineSegment(c,d,a) || + pointOnLineSegment(c,d,b); + return orientation(a, b, c) * orientation(a, b, d) <= 0 && + orientation(c, d, a) * orientation(c, d, b) <= 0; +} + +// Berechnet die Schnittpunkte der Strecken p0-p1 und p2-p3. +// Enthält entweder keinen Punkt, den einzigen Schnittpunkt +// oder die Endpunkte der Schnittstrecke. +vector<pt> lineSegmentIntersection(pt p0, pt p1, pt p2, pt p3) { + double a = cross(p1 - p0, p3 - p2); + double b = cross(p2 - p0, p3 - p2); + double c = cross(p1 - p0, p0 - p2); + if (a < 0) {a = -a; b = -b; c = -c;} + if (b < -EPS || a-b < -EPS || + c < -EPS || a-c < -EPS) return {}; + if (a > EPS) return {p0 + b/a*(p1 - p0)}; + vector<pt> result; + auto insertUnique = [&](pt p) { + for (auto q: result) if (abs(p - q) < EPS) return; + result.push_back(p); + }; + if (dot(p2-p0, p3-p0) < EPS) insertUnique(p0); + if (dot(p2-p1, p3-p1) < EPS) insertUnique(p1); + if (dot(p0-p2, p1-p2) < EPS) insertUnique(p2); + if (dot(p0-p3, p1-p3) < EPS) insertUnique(p3); + return result; +} + +// Entfernung von Punkt p zur Gearden durch a-b. 2d und 3d +double distToLine(pt a, pt b, pt p) { + return abs(cross(p - a, b - a)) / abs(b - a); +} + +// Liegt p auf der Geraden a-b? 2d und 3d +bool pointOnLine(pt a, pt b, pt p) { + return orientation(a, b, p) == 0; +} + +// Test auf Linienschnitt zwischen a-b und c-d. +bool lineIntersection(pt a, pt b, pt c, pt d) { + return abs(cross(a - b, c - d)) < EPS; +} + +// Berechnet den Schnittpunkt der Graden p0-p1 und p2-p3. +// die Graden dürfen nicht parallel sein! +pt lineIntersection(pt p0, pt p1, pt p2, pt p3) { + double a = cross(p1 - p0, p3 - p2); + double b = cross(p2 - p0, p3 - p2); + return {p0 + b/a*(p1 - p0)}; +} + +// Liegt p auf der Strecke a-b? +bool pointOnLineSegment(pt a, pt b, pt p) { + if (orientation(a, b, p) != 0) return false; + ld dist = norm(a - b); + return norm(a - p) <= dist && norm(b - p) <= dist; +} + +// Entfernung von Punkt p zur Strecke a-b. +double distToSegment(pt a, pt b, pt p) { + if (a == b) return abs(p - a); + pt dir = b - a; + if (dot(dir, a) <= dot(dir, p) && dot(dir, p) <= dot(dir, b)) { + return distToLine(a, b, p); + } else { + return min(abs(p - a), abs(p - b)); +}} + +// Kürzeste Entfernung zwischen den Strecken a-b und c-d. +double distBetweenSegments(pt a, pt b, pt c, pt d) { + if (lineSegmentIntersection(a, b, c, d)) return 0.0; + double result = distToSegment(a, b, c); + result = min(result, distToSegment(a, b, d)); + result = min(result, distToSegment(c, d, a)); + return min(result, distToSegment(c, d, b)); +} + +// sortiert alle Punkte pts auf einer Linie +// entsprechend der richtung dir 2d und 3d +void sortLine(pt dir, vector<pt>& pts) { + sort(pts.begin(), pts.end(), [&](pt a, pt b){ + return dot(dir, a) < dot(dir, b); + }); +}
\ No newline at end of file diff --git a/geometry/polygon.cpp b/geometry/polygon.cpp new file mode 100644 index 0000000..6a8080d --- /dev/null +++ b/geometry/polygon.cpp @@ -0,0 +1,33 @@ +// Berechnet den Flächeninhalt eines Polygons +// (nicht selbstschneidend). +// Punkte gegen den Uhrzeigersinn: positiv, sonst negativ. +double area(const vector<pt>& polygon) {//points unique + double res = 0; int n = sz(polygon); + for (int i = 0; i < n; i++) + res += cross(polygon[i], polygon[(i + 1) % n]); + return 0.5 * res; +} + +// Testet, ob ein Punkt im Polygon liegt (beliebige Polygone). +bool inside(pt p, const vector<pt>& polygon) {//points unique + pt rayEnd = p + pt(1, 1000000); + int counter = 0, n = sz(polygon); + for (int i = 0; i < n; i++) { + pt start = polygon[i], end = polygon[(i + 1) % n]; + if (lineSegmentIntersection(p, rayEnd, start, end)) { + counter++; + }} + return counter & 1; +} + +//convex hull without duplicates, h[0] == h.back() +bool inside(pt p, const vector<pt>& hull) { + if (orientation(hull[0], hull.back(), p) > 0) return false; + ll l = 0, r = sz(hull) - 1; + while (l + 1 < r) { + ll m = (l + r) / 2; + if (orientation(hull[0], hull[m], p) >= 0) l = m; + else r = m; + } + return orientation(hull[l], hull[r], p) >= 0; +} diff --git a/geometry/segmentIntersection.cpp b/geometry/segmentIntersection.cpp new file mode 100644 index 0000000..c2961da --- /dev/null +++ b/geometry/segmentIntersection.cpp @@ -0,0 +1,63 @@ +struct seg { + pt a, b; + int id; + bool operator<(const seg& o) const { + if (real(a) < real(o.a)) { + int s = orientation(a, b, o.a); + return (s > 0 || (s == 0 && imag(a) < imag(o.a)));// + } else if (real(a) > real(o.a)) { + int s = orientation(o.a, o.b, a); + return (s < 0 || (s == 0 && imag(a) < imag(o.a))); + } + return imag(a) < imag(o.a); + } +}; + +struct event { + pt p; + int id, type; + bool operator<(const event& o) const { + if (real(p) != real(o.p)) return real(p) < real(o.p); + if (type != o.type) return type > o.type; + return imag(p) < imag(o.p); + } +}; + +bool lessPT(const pt& a, const pt& b) { + return real(a) != real(b) ? real(a) < real(b) + : imag(a) < imag(b); +} + +bool intersect(const seg& a, const seg& b) { + return lineSegmentIntersection(a.a, a.b, b.a, b.b); +} + +pair<int, int> intersect(vector<seg>& segs) { + vector<event> events; + for (seg& s : segs) { + if (lessPT(s.b, s.a)) swap(s.b, s.a); + events.push_back({s.a, s.id, 1}); + events.push_back({s.b, s.id, -1}); + } + sort(all(events)); + + set<seg> q; + vector<set<seg>::iterator> where(sz(segs)); + for (auto e : events) { + int id = e.id; + if (e.type > 0) { + auto it = q.lower_bound(segs[id]); + if (it != q.end() && intersect(*it, segs[id])) + return {it->id, segs[id].id}; + if (it != q.begin() && intersect(*prev(it), segs[id])) + return {prev(it)->id, segs[id].id}; + where[id] = q.insert(it, segs[id]); + } else { + auto it = where[id]; + if (it != q.begin() && next(it) != q.end() && intersect(*next(it), *prev(it))) + return {next(it)->id, prev(it)->id}; + q.erase(it); + } + } + return {-1, -1}; +}
\ No newline at end of file diff --git a/geometry/sortAround.cpp b/geometry/sortAround.cpp new file mode 100644 index 0000000..a95d224 --- /dev/null +++ b/geometry/sortAround.cpp @@ -0,0 +1,10 @@ +bool left(pt p) {return real(p) < 0 ||
+ (real(p) == 0 && imag(p) < 0);}
+
+void sortAround(pt p, vector<pt>& ps) {
+ sort(all(ps), [&](const pt& a, const pt& b){
+ if (left(a - p) != left(b - p))
+ return left(a - p) > left(b - p);
+ return cross(p, a, b) > 0;
+ });
+}
\ No newline at end of file diff --git a/geometry/spheres.cpp b/geometry/spheres.cpp new file mode 100644 index 0000000..0657ab8 --- /dev/null +++ b/geometry/spheres.cpp @@ -0,0 +1,29 @@ +// Great Cirlce Distance mit Längen- und Breitengrad. +double gcDist(double pLat, double pLon, + double qLat, double qLon, double radius) { + pLat *= PI / 180; pLon *= PI / 180; + qLat *= PI / 180; qLon *= PI / 180; + return radius * acos(cos(pLat) * cos(pLon) * + cos(qLat) * cos(qLon) + + cos(pLat) * sin(pLon) * + cos(qLat) * sin(qLon) + + sin(pLat) * sin(qLat)); +} + +// Great Cirlce Distance mit kartesischen Koordinaten. +double gcDist(point p, point q) { + return acos(p.x * q.x + p.y * q.y + p.z * q.z); +} + +// 3D Punkt in kartesischen Koordinaten. +struct point{ + double x, y, z; + point() {} + point(double x, double y, double z) : x(x), y(y), z(z) {} + point(double lat, double lon) { + lat *= PI / 180.0; lon *= PI / 180.0; + x = cos(lat) * sin(lon); + y = cos(lat) * cos(lon); + z = sin(lat); + } +}; diff --git a/geometry/triangle.cpp b/geometry/triangle.cpp new file mode 100644 index 0000000..3a39302 --- /dev/null +++ b/geometry/triangle.cpp @@ -0,0 +1,40 @@ +// Mittelpunkt des Dreiecks abc. +pt centroid(pt a, pt b, pt c) {return (a + b + c) / 3.0;} + +// Flächeninhalt eines Dreicks bei bekannten Eckpunkten. +double area(pt a, pt b, pt c) { + return abs(cross(b - a, c - a)) / 2.0; +} + +// Flächeninhalt eines Dreiecks bei bekannten Seitenlängen. +double area(double a, double b, double c) { + double s = (a + b + c) / 2.0; + return sqrt(s * (s-a) * (s-b) * (s-c)); +} + +// Zentrum des Kreises durch alle Eckpunkte +pt outCenter(pt a, pt b, pt c) { + double d = 2.0 * (real(a) * imag(b-c) + + real(b) * imag(c-a) + + real(c) * imag(a-b)); + return (a*conj(a)*conj(b-c) + + b*conj(b)*conj(c-a) + + c*conj(c)*conj(a-b)) / d; +} + +// Zentrum des größten Kreises im Dreiecke +pt inCenter(pt a, pt b, pt c) { + double x = abs(a-b), y = abs(b-c), z = abs(a-c); + return (y*a + z*b + x*c) / (a+b+c); +} + + +// Sind die Dreiecke a1, b1, c1, and a2, b2, c2 ähnlich? +// Erste Zeile testet Ähnlichkeit mit gleicher Orientierung, +// zweite Zeile testet Ähnlichkeit mit verschiedener Orientierung +bool similar (pt a1, pt b1, pt c1, pt a2, pt b2, pt c2) { + return ((b2-a2) * (c1-a1) == (b1-a1) * (c2-a2) || + (b2-a2) * (conj(c1)-conj(a1)) == (conj(b1)-conj(a1)) + * (c2-a2) + ); +} diff --git a/graph/2sat.cpp b/graph/2sat.cpp index ab38b02..2ebb11a 100644 --- a/graph/2sat.cpp +++ b/graph/2sat.cpp @@ -1,58 +1,28 @@ struct sat2 { - vector<vector<int>> adjlist, sccs; - vector<bool> visited, inStack; - int n, sccCounter, dfsCounter; - vector<int> d, low, idx, sol; - stack<int> s; + int n; // + scc variablen + vector<int> sol; - sat2(int vars) : n(vars*2) { adjlist.resize(n); }; + sat2(int vars) : n(vars*2), adjlist(vars*2) {}; - static int var(int i) { return i << 1; } + static int var(int i) {return i << 1;} void addImpl(int v1, int v2) { adjlist[v1].push_back(v2); adjlist[1^v2].push_back(1^v1); } - void addEquiv(int v1, int v2) { addImpl(v1, v2); addImpl(v2, v1); } - void addOr(int v1, int v2) { addImpl(1^v1, v2); } - void addXor(int v1, int v2) { addOr(v1, v2); addOr(1^v1, 1^v2); } - void addTrue(int v1) { addImpl(1^v1, v1); } - void addFalse(int v1) { addTrue(1^v1); } - void addAnd(int v1, int v2) { addTrue(v1); addTrue(v2); } - void addNand(int v1, int v2) { addOr(1^v1, 1^v2); } - - void dfs(int v) { - visited[v] = true; - d[v] = low[v] = dfsCounter++; - s.push(v); inStack[v] = true; - - for (auto w : adjlist[v]) { - if (!visited[w]) { - dfs(w); - low[v] = min(low[v], low[w]); - } else if (inStack[w]) low[v] = min(low[v], low[w]); - } - - if (d[v] == low[v]) { - sccs.push_back(vector<int>()); - int w; - do { - w = s.top(); s.pop(); inStack[w] = false; - idx[w] = sccCounter; - sccs[sccCounter].push_back(w); - } while (w != v); - sccCounter++; - }} + void addEquiv(int v1, int v2) {addImpl(v1, v2); addImpl(v2, v1);} + void addOr(int v1, int v2) {addImpl(1^v1, v2);} + void addXor(int v1, int v2) {addOr(v1, v2); addOr(1^v1, 1^v2);} + void addTrue(int v1) {addImpl(1^v1, v1);} + void addFalse(int v1) {addTrue(1^v1);} + void addAnd(int v1, int v2) {addTrue(v1); addTrue(v2);} + void addNand(int v1, int v2) {addOr(1^v1, 1^v2);} bool solvable() { - visited.assign(n, false); - inStack.assign(n, false); - d.assign(n, -1); - low.assign(n, -1); - idx.assign(n, -1); - sccCounter = dfsCounter = 0; - for (int i = 0; i < n; i++) if (!visited[i]) dfs(i); - for (int i = 0; i < n; i += 2) if (idx[i] == idx[i + 1]) return false; + scc(); //scc code von oben + for (int i = 0; i < n; i += 2) { + if (idx[i] == idx[i + 1]) return false; + } return true; } diff --git a/graph/LCA.cpp b/graph/LCA.cpp new file mode 100644 index 0000000..bdb8f12 --- /dev/null +++ b/graph/LCA.cpp @@ -0,0 +1,24 @@ +vector<vector<int>> adjlist(); +vector<int> visited(); +vector<int> first(); +vector<int> depth(); + +void initLCA(int gi, int d, int& c) { + visited[c] = gi, depth[c] = d, first[gi] = min(c, first[gi]), c++; + for(int gn : adjlist[gi]) { + initLCA(gn, d+1, c); + visited[c] = gi, depth[c] = d, c++; +}} + +int getLCA(int a, int b) { + return visited[query(min(first[a], first[b]), max(first[a], first[b]))]; +} + +void exampleUse() { + int c = 0; + visited = vector<int>(2*adjlist.size()); + first = vector<int>(adjlist.size(), 2*adjlist.size()); + depth = vector<int>(2*adjlist.size()); + initLCA(0, 0, c); + init(depth); +} diff --git a/graph/LCA_sparse.cpp b/graph/LCA_sparse.cpp new file mode 100644 index 0000000..2a38528 --- /dev/null +++ b/graph/LCA_sparse.cpp @@ -0,0 +1,32 @@ +struct LCA { + vector<ll> depth; + vector<int> visited, first; + int idx; + SparseTable st; //sparse table von oben + + void init(vector<vector<int>>& g, int root) { + depth.assign(2 * sz(g), 0); + visited.assign(2 * sz(g), -1); + first.assign(sz(g), 2 * sz(g)); + idx = 0; + visit(g, root); + st.init(&depth); + } + + void visit(vector<vector<int>>& g, int v, ll d=0, int p=-1) { + visited[idx] = v, depth[idx] = d; + first[v] = min(idx, first[v]), idx++; + + for (int w : g[v]) { + if (first[w] == 2 * sz(g)) { + visit(g, w, d + 1, v); + visited[idx] = v, depth[idx] = d, idx++; + }}} + + int getLCA(int a, int b) { + if (first[a] > first[b]) swap(a, b); + return visited[st.queryIdempotent(first[a], first[b] + 1)]; + } + + ll getDepth(int a) {eturn depth[first[a]];} +}; diff --git a/graph/TSP.cpp b/graph/TSP.cpp index 7fba361..0f72766 100644 --- a/graph/TSP.cpp +++ b/graph/TSP.cpp @@ -1,23 +1,28 @@ -// Laufzeit: O(n^2*2^n) -vector<vector<int>> dist; // Entfernung zwischen je zwei Punkten. -vector<int> TSP() { - int n = dist.size(), m = 1 << n; - vector<vector<ii>> dp(n, vector<ii>(m, ii(INF, -1))); +vector<vector<ll>> dist; // Entfernung zwischen je zwei Punkten. + +void TSP() { + int n = sz(dist), m = 1 << n; + vector<vector<edge>> dp(n, vector<edge>(m, edge{INF, -1})); - for(int c = 0; c < n; c++) dp[c][m-1].first = dist[c][0], dp[c][m-1].second = 0; + for (int c = 0; c < n; c++) + dp[c][m-1].dist = dist[c][0], dp[c][m-1].to = 0; - for(int v = m - 2; v >= 0; v--) { - for(int c = n - 1; c >= 0; c--) { - for(int g = 0; g < n; g++) { - if(g != c && !((1 << g) & v)) { - if((dp[g][(v | (1 << g))].first + dist[c][g]) < dp[c][v].first) { - dp[c][v].first = dp[g][(v | (1 << g))].first + dist[c][g]; - dp[c][v].second = g; + for (int v = m - 2; v >= 0; v--) { + for (int c = n - 1; c >= 0; c--) { + for (int g = 0; g < n; g++) { + if (g != c && !((1 << g) & v)) { + if ((dp[g][(v | (1 << g))].dist + dist[c][g]) < + dp[c][v].dist) { + dp[c][v].dist = + dp[g][(v | (1 << g))].dist + dist[c][g]; + dp[c][v].to = g; }}}}} + // return dp[0][1]; // Länge der Tour - vector<int> res; res.push_back(0); int v = 0; - while(res.back() != 0 || res.size() == 1) { - res.push_back(dp[res.back()][(v |= (1 << res.back()))].second); - } - return res; // Enthält Knoten 0 zweimal. An erster und letzter Position. + vector<int> tour; tour.push_back(0); int v = 0; + while (tour.back() != 0 || sz(tour) == 1) + tour.push_back(dp[tour.back()] + [(v |= (1 << tour.back()))].to); + // Enthält Knoten 0 zweimal. An erster und letzter Position. + // return tour; } diff --git a/graph/articulationPoints.cpp b/graph/articulationPoints.cpp index fba08bb..e173355 100644 --- a/graph/articulationPoints.cpp +++ b/graph/articulationPoints.cpp @@ -1,33 +1,45 @@ -// Laufzeit: O(|V|+|E|) -vector<vector<int>> adjlist; +vector<vector<edge>> adjlist; +vector<int> num; +int counter, rootCount, root; vector<bool> isArt; -vector<int> d, low; -int counter, root, rootCount; // rootCount >= 2 <=> root Artikulationspunkt -vector<ii> bridges; // Nur fuer Brücken. +vector<edge> bridges, st; +vector<vector<edge>> bcc; -void dfs(int v, int parent = -1) { - d[v] = low[v] = ++counter; - if (parent == root) ++rootCount; - - for (auto w : adjlist[v]) { - if (!d[w]) { - dfs(w, v); - if (low[w] >= d[v] && v != root) isArt[v] = true; - if (low[w] > d[v]) bridges.push_back(ii(v, w)); - low[v] = min(low[v], low[w]); - } else if (w != parent) { - low[v] = min(low[v], d[w]); -}}} +int dfs(int v, int parent = -1) { + int me = num[v] = ++counter, top = me; + for (edge& e : adjlist[v]) { + if (e.id == parent){} + else if (num[e.to]) { + top = min(top, num[e.to]); + if (num[e.to] < me) st.push_back(e); + } else { + if (v == root) rootCount++; + int si = sz(st); + int up = dfs(e.to, e.id); + top = min(top, up); + if (up >= me) isArt[v] = true; + if (up > me) bridges.push_back(e); + if (up <= me) st.push_back(e); + if (up == me) { + bcc.emplace_back(); + while (sz(st) > si) { + bcc.back().push_back(st.back()); + st.pop_back(); + }}}} + return top; +} void findArticulationPoints() { counter = 0; - low.resize(adjlist.size()); - d.assign(adjlist.size(), 0); - isArt.assign(adjlist.size(), false); - bridges.clear(); //nur fuer Bruecken - for (int v = 0; v < (int)adjlist.size(); v++) { - if (!d[v]) { - root = v; rootCount = 0; + num.assign(sz(adjlist), 0); + isArt.assign(sz(adjlist), false); + bridges.clear(); + st.clear(); + bcc.clear(); + for (int v = 0; v < sz(adjlist); v++) { + if (!num[v]) { + root = v; + rootCount = 0; dfs(v); - if (rootCount > 1) isArt[v] = true; -}}} + isArt[v] = rootCount > 1; +}}}
\ No newline at end of file diff --git a/graph/bellmannFord.cpp b/graph/bellmannFord.cpp index d3d6094..21c7dbe 100644 --- a/graph/bellmannFord.cpp +++ b/graph/bellmannFord.cpp @@ -1,20 +1,19 @@ -// Laufzeit: O(|V|*|E|) -vector<edge> edges; // Kanten einfügen! -vector<int> dist, parent; - -void bellmannFord() { - dist.assign(NUM_VERTICES, INF); dist[0] = 0; - parent.assign(NUM_VERTICES, -1); - for (int i = 0; i < NUM_VERTICES - 1; i++) { - for (auto &e : edges) { - if (dist[e.from] + e.cost < dist[e.to]) { +void bellmannFord(int n, vector<edge> edges, int start) { + vector<ll> dist(n, INF), parent(n, -1); + dist[start] = 0; + + for (int i = 1; i < n; i++) { + for (edge& e : edges) { + if (dist[e.from] != INF && + dist[e.from] + e.cost < dist[e.to]) { dist[e.to] = dist[e.from] + e.cost; parent[e.to] = e.from; }}} - // "dist" und "parent" sind korrekte kürzeste Pfade. - // Folgende Zeilen prüfen nur negative Kreise. - for (auto &e : edges) { - if (dist[e.from] + e.cost < dist[e.to]) { + for (edge& e : edges) { + if (dist[e.from] != INF && + dist[e.from] + e.cost < dist[e.to]) { // Negativer Kreis gefunden. -}}} + }} + //return dist, parent; +} diff --git a/graph/bitonicTSP.cpp b/graph/bitonicTSP.cpp index 2a8b5f9..767bc5b 100644 --- a/graph/bitonicTSP.cpp +++ b/graph/bitonicTSP.cpp @@ -1,24 +1,31 @@ -// Laufzeit: O(n^2) -vector<vector<double>> dp, dist; // Entfernungen zwischen Punkten. +vector<vector<double>> dist; // Initialisiere mit Entfernungen zwischen Punkten. -double get(int p1, int p2) { - int v = max(p1, p2) + 1; - if (v == dist.size()) return dist[p1][v - 1] + dist[p2][v - 1]; - if (dp[p1][p2] >= 0.0) return dp[p1][p2]; - double tryLR = dist[p1][v] + get(v, p2); - double tryRL = dist[p2][v] + get(p1, v); - return dp[p1][p2] = min(tryLR, tryRL); -} +void bitonicTSP() { + vector<double> dp(sz(dist), HUGE_VAL); + vector<int> pre(sz(dist)); // nur für Tour + dp[0] = 0; dp[1] = 2 * dist[0][1]; pre[1] = 0; + for (unsigned int i = 2; i < sz(dist); i++) { + double link = 0; + for (int j = i - 2; j >= 0; j--) { + link += dist[j + 1][j + 2]; + double opt = link + dist[j][i] + dp[j + 1] - dist[j][j + 1]; + if (opt < dp[i]) { + dp[i] = opt; + pre[i] = j; + }}} + // return dp.back(); // Länger der Tour -void bitonicTour() { - dp.assign(dist.size(), vector<double>(dist.size(), -1)); - get(0, 0); // return dp[0][0]; // Länger der Tour - vector<int> lr = {0}, rl = {0}; - for (int p1 = 0, p2 = 0, v; (v = max(p1, p2) + 1) < dist.size();) { - if (dp[p1][p2] == dist[p1][v] + dp[v][p2]) { - lr.push_back(v); p1 = v; - } else { - rl.push_back(v); p2 = v; - }} - lr.insert(lr.end(), rl.rbegin(), rl.rend()); // Tour, Knoten 0 doppelt. -} + int j, n = sz(dist) - 1; + vector<int> ut, lt = {n, n - 1}; + do { + j = pre[n]; + (lt.back() == n ? lt : ut).push_back(j); + for (int i = n - 1; i > j + 1; i--) { + (lt.back() == i ? lt : ut).push_back(i - 1); + } + } while(n = j + 1, j > 0); + (lt.back() == 1 ? lt : ut).push_back(0); + reverse(lt.begin(), lt.end()); + lt.insert(lt.end(), ut.begin(), ut.end()); + //return lt;// Enthält Knoten 0 zweimal. An erster und letzter Position. +}
\ No newline at end of file diff --git a/graph/bitonicTSPsimple.cpp b/graph/bitonicTSPsimple.cpp new file mode 100644 index 0000000..ff605d9 --- /dev/null +++ b/graph/bitonicTSPsimple.cpp @@ -0,0 +1,28 @@ +vector<vector<double>> dist; // Entfernungen zwischen Punkten. +vector<vector<double>> dp; + +double get(int p1, int p2) { + int v = max(p1, p2) + 1; + if (v == sz(dist)) return dist[p1][v - 1] + dist[p2][v - 1]; + if (dp[p1][p2] >= 0.0) return dp[p1][p2]; + double tryLR = dist[p1][v] + get(v, p2); + double tryRL = dist[p2][v] + get(p1, v); + return dp[p1][p2] = min(tryLR, tryRL); +} + +void bitonicTour() { + dp = vector<vector<double>>(sz(dist), + vector<double>(sz(dist), -1)); + get(0, 0); + // return dp[0][0]; // Länger der Tour + vector<int> lr = {0}, rl = {0}; + for (int p1 = 0, p2 = 0, v; (v = max(p1, p2)+1) < sz(dist);) { + if (dp[p1][p2] == dist[p1][v] + dp[v][p2]) { + lr.push_back(v); p1 = v; + } else { + rl.push_back(v); p2 = v; + }} + lr.insert(lr.end(), rl.rbegin(), rl.rend()); + // Enthält Knoten 0 zweimal. An erster und letzter Position. + // return lr; +}
\ No newline at end of file diff --git a/graph/blossom.cpp b/graph/blossom.cpp new file mode 100644 index 0000000..cc19a2b --- /dev/null +++ b/graph/blossom.cpp @@ -0,0 +1,84 @@ +struct GM { + vector<vector<int>> adjlist; + // pairs ist der gematchte knoten oder n + vector<int> pairs, first, que; + vector<pair<int, int>> label; + int head, tail; + + GM(int n) : adjlist(n), pairs(n + 1, n), first(n + 1, n), + que(n), label(n + 1, {-1, -1}) {} + + void rematch(int v, int w) { + int t = pairs[v]; pairs[v] = w; + if (pairs[t] != v) return; + if (label[v].second == -1) { + pairs[t] = label[v].first; + rematch(pairs[t], t); + } else { + int x = label[v].first; + int y = label[v].second; + rematch(x, y); + rematch(y, x); + }} + + int findFirst(int u) { + return label[first[u]].first < 0 ? first[u] + : first[u] = findFirst(first[u]); + } + + void relabel(int x, int y) { + int r = findFirst(x); + int s = findFirst(y); + if (r == s) return; + auto h = label[r] = label[s] = {~x, y}; + int join; + while (true) { + if (s != sz(adjlist)) swap(r, s); + r = findFirst(label[pairs[r]].first); + if (label[r] == h) { + join = r; + break; + } else { + label[r] = h; + }} + for (int v : {first[x], first[y]}) { + for (; v != join; v = first[label[pairs[v]].first]) { + label[v] = {x, y}; + first[v] = join; + que[tail++] = v; + }}} + + bool augment(int u) { + label[u] = {sz(adjlist), -1}; + first[u] = sz(adjlist); + head = tail = 0; + for (que[tail++] = u; head < tail;) { + int x = que[head++]; + for (int y : adjlist[x]) { + if (pairs[y] == sz(adjlist) && y != u) { + pairs[y] = x; + rematch(x, y); + return true; + } else if (label[y].first >= 0) { + relabel(x, y); + } else if (label[pairs[y]].first == -1) { + label[pairs[y]].first = x; + first[pairs[y]] = y; + que[tail++] = pairs[y]; + }}} + return false; + } + + int match() { + int matching = head = tail = 0; + for (int u = 0; u < sz(adjlist); u++) { + if (pairs[u] < sz(adjlist) || !augment(u)) continue; + matching++; + for (int i = 0; i < tail; i++) + label[que[i]] = label[pairs[que[i]]] = {-1, -1}; + label[sz(adjlist)] = {-1, -1}; + } + return matching; + } + +};
\ No newline at end of file diff --git a/graph/bronKerbosch.cpp b/graph/bronKerbosch.cpp new file mode 100644 index 0000000..caf2421 --- /dev/null +++ b/graph/bronKerbosch.cpp @@ -0,0 +1,24 @@ +using bits = bitset<64>; +vector<bits> adj, cliques; + +void addEdge(int a, int b) { + if (a != b) adj[a][b] = adj[b][a] = 1; +} + +void bronKerboschRec(bits R, bits P, bits X) { + if (!P.any() && !X.any()) { + cliques.push_back(R); + } else { + int q = (P | X)._Find_first(); + bits cands = P & ~adj[q]; + for (int i = 0; i < sz(adj); i++) if (cands[i]){ + R[i] = 1; + bronKerboschRec(P & adj[i], X & adj[i], R); + R[i] = P[i] = 0; + X[i] = 1; +}}} + +void bronKerbosch() { + cliques.clear(); + bronKerboschRec({}, {(1ull << sz(adj)) - 1}, {}); +} diff --git a/graph/capacityScaling.cpp b/graph/capacityScaling.cpp index 8571e2b..b59c322 100644 --- a/graph/capacityScaling.cpp +++ b/graph/capacityScaling.cpp @@ -1,35 +1,44 @@ -// Ford Fulkerson mit Capacity Scaling. Laufzeit: O(|E|^2*log(C)) -static const int MAX_N = 500; // #Knoten, egal für die Laufzeit. -struct edge { int dest, rev; ll cap, flow; }; -vector<edge> adjlist[MAX_N]; -int visited[MAX_N], target, dfsCounter; +struct edge { + int from, to; + ll f, c; +}; + +vector<edge> edges; +vector<vector<int>> adjlist; +int s, t, dfsCounter; +vector<int> visited; ll capacity; -bool dfs(int x) { - if (x == target) return 1; - if (visited[x] == dfsCounter) return 0; - visited[x] = dfsCounter; - for (edge &e : adjlist[x]) { - if (e.cap >= capacity && dfs(e.dest)) { - e.cap -= capacity; adjlist[e.dest][e.rev].cap += capacity; - e.flow += capacity; adjlist[e.dest][e.rev].flow -= capacity; - return 1; - }} - return 0; +void addEdge(int from, int to, ll c) { + adjlist[from].push_back(edges.size()); + edges.push_back({from, to, 0, c}); + adjlist[to].push_back(edges.size()); + edges.push_back({to, from, 0, 0}); } -void addEdge(int u, int v, ll c) { - adjlist[u].push_back(edge {v, (int)adjlist[v].size(), c, 0}); - adjlist[v].push_back(edge {u, (int)adjlist[u].size() - 1, 0, 0}); +bool dfs(int x) { + if (x == t) return true; + if (visited[x] == dfsCounter) return false; + visited[x] = dfsCounter; + for (int id : adjlist[x]) { + if (edges[id].c >= capacity && dfs(edges[id].to)) { + edges[id].c -= capacity; edges[id ^ 1].c += capacity; + edges[id].f += capacity; edges[id ^ 1].f -= capacity; + return true; + }} + return false; } -ll maxFlow(int s, int t) { - capacity = 1L << 62; - target = t; - ll flow = 0L; - while (capacity) { - while (dfsCounter++, dfs(s)) flow += capacity; - capacity /= 2; - } - return flow; +ll maxFlow(int source, int target) { + capacity = 1ll << 62; + s = source; + t = target; + ll flow = 0; + visited.assign(adjlist.size(), 0); + dfsCounter = 0; + while (capacity) { + while (dfsCounter++, dfs(s)) flow += capacity; + capacity /= 2; + } + return flow; } diff --git a/graph/centroid.cpp b/graph/centroid.cpp new file mode 100644 index 0000000..d2855e2 --- /dev/null +++ b/graph/centroid.cpp @@ -0,0 +1,22 @@ +vector<int> s; +void dfs1(int u, int v = -1) { + s[u] = 1; + for (int w : adj[u]) { + if (w == v) continue; + dfs1(w, u); + s[u] += s[w]; +}} + +pair<int, int> dfs2(int u, int v, int n) { + for (int w : adj[u]) { + if (2 * s[w] == n) return {u, w}; + if (w != v && 2 * s[w] > n) return dfs2(w, u, n); + } + return {u, -1}; +} + +pair<int, int> find_centroid(int root) { + // s muss nicht initialisiert werden, nur groß genug sein + dfs1(root); + return dfs2(root, -1, s[root]); +} diff --git a/graph/connect.cpp b/graph/connect.cpp new file mode 100644 index 0000000..28a6f6c --- /dev/null +++ b/graph/connect.cpp @@ -0,0 +1,31 @@ +struct connect { + int n; + vector<pair<int, int>> edges; + LCT lct; // min LCT no updates required + + connect(int n, int m) : n(n), edges(m), lct(n+m) {} + + bool connected(int a, int b) { + return lct.connected(&lct.nodes[a], &lct.nodes[b]); + } + + void addEdge(int a, int b, int id) { + lct.nodes[id + n] = LCT::Node(id + n, id + n); + edges[id] = {a, b}; + if (connected(a, b)) { + int old = lct.query(&lct.nodes[a], &lct.nodes[b]); + if (old < id) eraseEdge(old); + } + if (!connected(a, b)) { + lct.link(&lct.nodes[a], &lct.nodes[id + n]); + lct.link(&lct.nodes[b], &lct.nodes[id + n]); + }} + + void eraseEdge(ll id) { + if (connected(edges[id].first, edges[id].second) && + lct.query(&lct.nodes[edges[id].first], + &lct.nodes[edges[id].second]) == id) { + lct.cut(&lct.nodes[edges[id].first], &lct.nodes[id + n]); + lct.cut(&lct.nodes[edges[id].second], &lct.nodes[id + n]); + }} +};
\ No newline at end of file diff --git a/graph/cycleCounting.cpp b/graph/cycleCounting.cpp new file mode 100644 index 0000000..800f27e --- /dev/null +++ b/graph/cycleCounting.cpp @@ -0,0 +1,65 @@ +constexpr ll maxEdges = 128; +using cycle = bitset<maxEdges>; +struct cylces { + ll n; + vector<vector<pair<ll, ll>>> adj; + vector<bool> seen; + vector<cycle> paths, base; + vector<pair<ll, ll>> edges; + + cylces(ll n) : n(n), adj(n), seen(n), paths(n) {} + + void addEdge(ll a, ll b) { + adj[a].push_back({b, sz(edges)}); + adj[b].push_back({a, sz(edges)}); + edges.push_back({a, b}); + } + + void addBase(cycle cur) { + for (cycle o : base) { + o ^= cur; + if (o._Find_first() > cur._Find_first()) cur = o; + } + if (cur.any()) base.push_back(cur); + } + + void findBase(ll c = 0, ll p = -1, cycle cur = {}) { + if (n == 0) return; + if (seen[c]) { + addBase(cur ^ paths[c]); + } else { + seen[c] = true; + paths[c] = cur; + for (auto e : adj[c]) { + if (e.first == p) continue; + cur[e.second].flip(); + findBase(e.first, c, cur); + cur[e.second].flip(); + }}} + + //cycle must be constrcuted from base + bool isCycle(cycle cur) { + if (cur.none()) return false; + init(n); + for (ll i = 0; i < sz(edges); i++) { + if (cur[i]) { + cur[i] = false; + if (findSet(edges[i].first) == + findSet(edges[i].second)) break; + unionSets(edges[i].first, edges[i].second); + }} + return cur.none(); + }; + + ll count() { + findBase(); + ll res = 0; + for (ll i = 1; i < (1ll << sz(base)); i++) { + cycle cur; + for (ll j = 0; j < sz(base); j++) { + if (((i >> j) & 1) != 0) cur ^= base[j]; + if (isCycle(cur)) res++; + } + return res; + } +}; diff --git a/graph/dfs.tex b/graph/dfs.tex new file mode 100644 index 0000000..1e6705f --- /dev/null +++ b/graph/dfs.tex @@ -0,0 +1,16 @@ +\begin{expandtable} +\begin{tabularx}{\linewidth}{|X|XIXIX|} + \hline + Kantentyp $(v, w)$ & \code{dfs[v] < dfs[w]} & \code{fin[v] > fin[w]} & \code{seen[w]} \\ + %$(v, w)$ & \code{dfs[w]} & \code{fin[w]} & \\ + \hline + in-tree & \code{true} & \code{true} & \code{false} \\ + \grayhline + forward & \code{true} & \code{true} & \code{true} \\ + \grayhline + backward & \code{false} & \code{false} & \code{true} \\ + \grayhline + cross & \code{false} & \code{true} & \code{true} \\ + \hline +\end{tabularx} +\end{expandtable} diff --git a/graph/dijkstra.cpp b/graph/dijkstra.cpp index 52cb57e..0b821dd 100644 --- a/graph/dijkstra.cpp +++ b/graph/dijkstra.cpp @@ -1,17 +1,21 @@ -// Laufzeit: O((|E|+|V|)*log |V|) -void dijkstra(int start) { - priority_queue<ii, vector<ii>, greater<ii> > pq; - vector<int> dist(NUM_VERTICES, INF), parent(NUM_VERTICES, -1); - dist[start] = 0; pq.push(ii(0, start)); +using path = pair<ll, int>; //dist, destination + +void dijkstra(const vector<vector<path>> &adjlist, int start) { + priority_queue<path, vector<path>, greater<path>> pq; + vector<ll> dist(sz(adjlist), INF); + vector<int> prev(sz(adjlist), -1); + dist[start] = 0; pq.emplace(0, start); while (!pq.empty()) { - ii front = pq.top(); pq.pop(); - int curNode = front.second, curDist = front.first; - if (curDist > dist[curNode]) continue; // WICHTIG! - - for (auto n : adjlist[curNode]) { - int nextNode = n.first, nextDist = curDist + n.second; - if (nextDist < dist[nextNode]) { - dist[nextNode] = nextDist; parent[nextNode] = curNode; - pq.push(ii(nextDist, nextNode)); -}}}} + path front = pq.top(); pq.pop(); + if (front.first > dist[front.second]) continue; // WICHTIG! + + for (path e : adjlist[front.second]) { + ll newDist = front.first + e.first; + if (newDist < dist[e.second]) { + dist[e.second] = newDist; + prev[e.second] = front.second; + pq.emplace(newDist, e.second); + }}} + //return dist, prev; +} diff --git a/graph/dinicScaling.cpp b/graph/dinicScaling.cpp index ebbcc27..1b58d29 100644 --- a/graph/dinicScaling.cpp +++ b/graph/dinicScaling.cpp @@ -1,61 +1,63 @@ -// Laufzeit: O(|V|^2*|E|) -// Knoten müssen von 0 nummeriert sein. -const int INF = 0x3FFFFFFF, MAXN = 500; -struct edge { int a, b; ll f, c; }; -int n, m, pt[MAXN], d[MAXN], s, t; -vector<edge> e; -vector<int> g[MAXN]; -ll flow = 0, lim; +struct edge { + int from, to; + ll f, c; +}; + +vector<edge> edges; +vector<vector<int>> adjlist; +int s, t; +vector<int> pt, dist; +ll flow, lim; queue<int> q; -void addEdge(int a, int b, ll c) { - g[a].push_back(e.size()); - e.push_back(edge {a, b, 0, c}); - g[b].push_back(e.size()); - e.push_back(edge {b, a, 0, 0}); +void addEdge(int from, int to, ll c) { + adjlist[from].push_back(sz(edges)); + edges.push_back({from, to, 0, c}); + adjlist[to].push_back(sz(edges)); + edges.push_back({to, from, 0, 0}); } bool bfs() { - for (int i = 0; i < n; i++) d[i] = INF; - d[s] = 0; - q.push(s); - while (!q.empty() && d[t] == INF) { - int cur = q.front(); q.pop(); - for (int i = 0; i < (int)g[cur].size(); i++) { - int id = g[cur][i], to = e[id].b; - if (d[to] == INF && e[id].c - e[id].f >= lim) { - d[to] = d[cur] + 1; - q.push(to); - } - } - } - while (!q.empty()) q.pop(); - return d[t] != INF; + dist.assign(sz(dist), -1); + dist[t] = sz(adjlist) + 1; + q.push(t); + while (!q.empty() && dist[s] < 0) { + int cur = q.front(); q.pop(); + for (int id : adjlist[cur]) { + int to = edges[id].to; + if (dist[to] < 0 && + edges[id ^ 1].c - edges[id ^ 1].f >= lim) { + dist[to] = dist[cur] - 1; + q.push(to); + }}} + while (!q.empty()) q.pop(); + return dist[s] >= 0; } bool dfs(int v, ll flow) { - if (flow == 0) return false; - if (v == t) return true; - for (; pt[v] < (int)g[v].size(); pt[v]++) { - int id = g[v][pt[v]], to = e[id].b; - if (d[to] == d[v] + 1 && e[id].c - e[id].f >= flow) { - int pushed = dfs(to, flow); - if (pushed) { - e[id].f += flow; - e[id ^ 1].f -= flow; - return true; - } - } - } - return false; + if (flow == 0) return false; + if (v == t) return true; + for (; pt[v] < sz(adjlist[v]); pt[v]++) { + int id = adjlist[v][pt[v]], to = edges[id].to; + if (dist[to] == dist[v] + 1 && + edges[id].c - edges[id].f >= flow) { + if (dfs(to, flow)) { + edges[id].f += flow; + edges[id ^ 1].f -= flow; + return true; + }}} + return false; } -// Nicht vergessen, s und t zu setzen! -void dinic() { - for (lim = (1LL << 62); lim >= 1;) { - if (!bfs()) { lim /= 2; continue; } - for (int i = 0; i < n; i++) pt[i] = 0; - int pushed; - while ((pushed = dfs(s, lim))) flow += lim; - } +ll maxFlow(int source, int target) { + s = source; + t = target; + flow = 0; + dist.resize(sz(adjlist)); + for (lim = (1LL << 62); lim >= 1;) { + if (!bfs()) {lim /= 2; continue;} + pt.assign(sz(adjlist), 0); + while (dfs(s, lim)) flow += lim; + } + return flow; } diff --git a/graph/euler.cpp b/graph/euler.cpp index a35ce13..0907ab2 100644 --- a/graph/euler.cpp +++ b/graph/euler.cpp @@ -1,40 +1,26 @@ -// Laufzeit: O(|V|+|E|) -vector< vector<int> > adjlist, otherIdx; -vector<int> cycle, validIdx; +vector<vector<int>> idx; +vector<int> to, validIdx, cycle; +vector<bool> used; -// Vertauscht Kanten mit Indizes a und b von Knoten n. -void swapEdges(int n, int a, int b) { - int neighA = adjlist[n][a], neighB = adjlist[n][b]; - int idxNeighA = otherIdx[n][a], idxNeighB = otherIdx[n][b]; - swap(adjlist[n][a], adjlist[n][b]); - swap(otherIdx[n][a], otherIdx[n][b]); - otherIdx[neighA][idxNeighA] = b; - otherIdx[neighB][idxNeighB] = a; -} - -// Entfernt Kante i von Knoten n (und die zugehörige Rückwärtskante). -void removeEdge(int n, int i) { - int other = adjlist[n][i]; - if (other == n) { //Schlingen. - validIdx[n]++; - return; - } - int otherIndex = otherIdx[n][i]; - validIdx[n]++; - if (otherIndex != validIdx[other]) { - swapEdges(other, otherIndex, validIdx[other]); - } - validIdx[other]++; +void addEdge(int a, int b) { + idx[a].push_back(sz(to)); + to.push_back(b); + used.push_back(false); + idx[b].push_back(sz(to)); //für ungerichtet + to.push_back(a); + used.push_back(false); } // Findet Eulerzyklus an Knoten n startend. -// Teste vorher, dass Graph zusammenhängend ist! Isolierten Knoten? -// Teste vorher, ob Eulerzyklus überhaupt existiert! +// init idx und validIdx void euler(int n) { - while (validIdx[n] < (int)adjlist[n].size()) { - int nn = adjlist[n][validIdx[n]]; - removeEdge(n, validIdx[n]); - euler(nn); - } - cycle.push_back(n); // Zyklus in cycle in umgekehrter Reihenfolge. + for (;validIdx[n] < sz(idx[n]); validIdx[n]++) { + if (!used[idx[n][validIdx[n]]]) { + int nn = to[idx[n][validIdx[n]]]; + used[idx[n][validIdx[n]]] = true; + used[idx[n][validIdx[n]] ^ 1] = true; //für ungerichtet + euler(nn); + }} + // Zyklus in cycle in umgekehrter Reihenfolge. + cycle.push_back(n); } diff --git a/graph/floydWarshall.cpp b/graph/floydWarshall.cpp index e9cd526..fb6263e 100644 --- a/graph/floydWarshall.cpp +++ b/graph/floydWarshall.cpp @@ -1,9 +1,26 @@ -// Initialisiere mat: mat[i][i] = 0, mat[i][j] = INF falls i & j nicht -// adjazent, Länge sonst. Laufzeit: O(|V|^3) +vector<vector<ll>> dist; // Entfernung zwischen je zwei Punkten. +vector<vector<int>> pre; + void floydWarshall() { - for (k = 0; k < MAX_V; k++) { - for (i = 0; i < MAX_V; i++) { - for (j = 0; j < MAX_V; j++) { - if (mat[i][k] != INF && mat[k][j] != INF && mat[i][k] + mat[k][j] < mat[i][j]) { - mat[i][j] = mat[i][k] + mat[k][j]; + pre.assign(sz(dist), vector<int>(sz(dist), -1)); + for (int i = 0; i < sz(dist); i++) { + for (int j = 0; j < sz(dist); j++) { + if (dist[i][j] < INF) { + pre[i][j] = j; + }}} + + for (int k = 0; k < sz(dist); k++) { + for (int i = 0; i < sz(dist); i++) { + for (int j = 0; j < sz(dist); j++) { + if (dist[i][j] > dist[i][k] + dist[k][j]) { + dist[i][j] = dist[i][k] + dist[k][j]; + pre[i][j] = pre[i][k]; }}}}} + +vector<int> getPath(int u, int v) { + //return dist[u][v]; // Pfadlänge u -> v + if (pre[u][v] < 0) return {}; + vector<int> path = {v}; + while (u != v) path.push_back(u = pre[u][v]); + return path; //Pfad u -> v +} diff --git a/graph/graph.tex b/graph/graph.tex index 37356f6..a26661d 100644 --- a/graph/graph.tex +++ b/graph/graph.tex @@ -1,90 +1,192 @@ \section{Graphen} -% \subsection{Minimale Spannbäume} +\begin{algorithm}{DFS} + \input{graph/dfs} +\end{algorithm} -% \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.) +\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} -% \paragraph{Kreiseigenschaft} -% Für jeden Kreis $K$ im Graphen gilt: -% Die schwerste Kante auf dem Kreis ist nicht Teil des minimalen Spannbaums. +\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}{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)$ + \begin{methods} + \method{havelHakimi}{findet Graph}{(\abs{V}+\abs{E})\cdot\log(\abs{V})} + \end{methods} + \sourcecode{graph/havelHakimi.cpp} +\end{algorithm} + +\begin{algorithm}{Centroids} + \begin{methods} + \method{find\_centroid}{findet alle Centroids des Baums (maximal 2)}{\abs{V}} + \end{methods} + \sourcecode{graph/centroid.cpp} +\end{algorithm} + +\begin{algorithm}{Baum-Isomorphie} + \begin{methods} + \method{getTreeLabel}{berechnet kanonischen Namen für einen Baum}{\abs{V}} + \end{methods} + \sourcecode{graph/treeIsomorphism.cpp} +\end{algorithm} \subsection{Kürzeste Wege} -% \subsubsection{Algorithmus von \textsc{Dijkstra}} -% Kürzeste Pfade in Graphen ohne negative Kanten. -\lstinputlisting{graph/dijkstra.cpp} - -% \subsubsection{\textsc{Bellmann-Ford}-Algorithmus} -% Kürzestes Pfade in Graphen mit negativen Kanten. -% Erkennt negative Zyklen. -\lstinputlisting{graph/bellmannFord.cpp} - -% \subsubsection{\textsc{Floyd-Warshall}-Algorithmus} -% \lstinputlisting{graph/floydWarshall.cpp} -Floyd Warshall: -\begin{itemize}[nosep] - \item Nur negative Werte sollten die Nullen bei Schlingen überschreiben. - \item Von parallelen Kanten sollte nur die günstigste gespeichert werden. - \item \lstinline{i} liegt genau dann auf einem negativen Kreis, wenn \lstinline{dist[i][i] < 0} ist. - \item Wenn für \lstinline{c} gilt, dass \lstinline{dist[u][c] != INF && dist[c][v] != INF && dist[c][c] < 0}, wird der u-v-Pfad beliebig kurz. -\end{itemize} +\subsubsection{Algorithmus von \textsc{Dijkstra}} +\method{dijkstra}{kürzeste Pfade in Graphen ohne negative Kanten}{\abs{E}\*\log(\abs{V})} +\sourcecode{graph/dijkstra.cpp} + +\subsubsection{\textsc{Bellmann-Ford}-Algorithmus} +\method{bellmanFord}{kürzeste Pfade oder negative Kreise finden}{\abs{V}\*\abs{E}} +\sourcecode{graph/bellmannFord.cpp} -\subsection{Strongly Connected Components (\textsc{Tarjans}-Algorithmus)} -\lstinputlisting{graph/scc.cpp} - -\subsection{Artikulationspunkte und Brücken} -\lstinputlisting{graph/articulationPoints.cpp} - -\subsection{Eulertouren} -\begin{itemize}[nosep] - \item Zyklus existiert, wenn jeder Knoten geraden Grad hat (ungerichtet), bzw. bei jedem Knoten Ein- und Ausgangsgrad übereinstimmen (gerichtet). - \item Pfad existiert, wenn alle bis auf (maximal) zwei Knoten geraden Grad haben (ungerichtet), bzw. bei allen Knoten bis auf zwei Ein- und Ausgangsgrad übereinstimmen, wobei einer eine Ausgangskante mehr hat (Startknoten) und einer eine Eingangskante mehr hat (Endknoten). - \item \textbf{Je nach Aufgabenstellung überprüfen, wie isolierte Punkte interpretiert werden sollen.} - \item Der Code unten läuft in Linearzeit. - Wenn das nicht notwenidg ist (oder bestimmte Sortierungen verlangt werden), gehts mit einem \lstinline{set} einfacher. - \item Algorithmus schlägt nicht fehl, falls kein Eulerzyklus existiert. - Die Existenz muss separat geprüft werden. +\subsubsection{\textsc{Floyd-Warshall}-Algorithmus} +\method{floydWarshall}{kürzeste Pfade oder negative Kreise finden}{\abs{V}^3} +\begin{itemize} + \item \code{dist[i][i] = 0, dist[i][j] = edge\{j, j\}.weight} oder \code{INF} + \item \code{i} liegt auf einem negativen Kreis $\Leftrightarrow$ \code{dist[i][i] < 0}. \end{itemize} -\begin{lstlisting} -VISIT(v): - forall e=(v,w) in E - delete e from E - VISIT(w) - print e -\end{lstlisting} -\lstinputlisting{graph/euler.cpp} - -\subsection{Lowest Common Ancestor} -\lstinputlisting{graph/lca.cpp} +\sourcecode{graph/floydWarshall.cpp} -\subsection{Max-Flow} +\subsubsection{Matrix-Algorithmus} +Sei $d_{ij}$ die Distanzmatrix von $G$, dann gibt $d_{ij}^k$ die kürzeste Distanz von $i$ nach $j$ mit maximal $k$ kanten an mit der Verknüpfung: $c_{ij} = a_{ij} * b_{ij} = \min\{a_{ik} + b_{kj}\}$ + + +Sei $a_{ij}$ die Adjazenzmatrix von $G$ \textcolor{gray}{(mit $a_{ii} = 1$)}, dann gibt $a_{ij}^k$ die Anzahl der Wege von $i$ nach $j$ mit Länge genau \textcolor{gray}{(maximal)} $k$ an mit der Verknüpfung: $c_{ij} = a_{ij} \* b_{ij} = \sum a_{ik} + b_{kj}$ + +\begin{algorithm}{Artikulationspunkte, Brücken und BCC} + \begin{methods} + \method{findArticulationPoints}{berechnet Artikulationspunkte,}{\abs{V}+\abs{E}} + \method{}{Brücken und BCC}{} + \end{methods} + \textbf{Wichtig:} isolierte Knoten und Brücken sind keine BCC. + \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} + +\begin{algorithm}{Eulertouren} + \begin{methods} + \method{euler}{berechnet den Kreis}{\abs{V}+\abs{E}} + \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>> adjlist} leichter + \item \textbf{Wichtig:} Algorithmus schlägt nicht fehl, falls kein Eulerzyklus existiert. + Die Existenz muss separat geprüft werden. + \end{itemize} +\end{algorithm} + +\begin{algorithm}{Cycle Counting} + \begin{methods} + \method{findBase}{berechnet Basis}{\abs{V}\cdot\abs{E}} + \method{count}{zählt Zykel}{2^{\abs{\mathit{base}}}} + \end{methods} + \begin{itemize} + \item jeder Zyklus ist das xor von einträgen in \code{base}. + \end{itemize} + \sourcecode{graph/cycleCounting.cpp} +\end{algorithm} + +\begin{algorithm}{Lowest Common Ancestor} + \begin{methods} + \method{init}{baut DFS-Baum über $g$ auf}{\abs{V}\*\log(\abs{V})} + \method{getLCA}{findet LCA}{1} + \method{getDepth}{berechnet Distanz zur Wurzel im DFS-Baum}{1} + \end{methods} + \sourcecode{graph/LCA_sparse.cpp} +\end{algorithm} + +\begin{algorithm}{Heavy-Light Decomposition} + \begin{methods} + \method{get\_intervals}{gibt Zerlegung des Pfades von $u$ nach $v$}{\log(\abs{V})} + \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} +\end{algorithm} + +\begin{algorithm}{Dynamic Connectivity} + \begin{methods} + \method{Constructor}{erzeugt Baum ($n$ Knoten, $m$ updates)}{n+m} + \method{addEdge}{fügt Kannte ein,\texttt{id}=delete Zeitpunkt}{\log(n)} + \method{eraseEdge}{entfernt Kante \texttt{id}}{\log(n)} + \end{methods} + \sourcecode{graph/connect.cpp} +\end{algorithm} + +\begin{algorithm}{Global Mincut} + \begin{methods} + \method{stoer\_wagner}{berechnet globalen Mincut}{\abs{V}^2\*\log(\abs{E})} + \method{merge(a,b)}{merged Knoten $b$ in Knoten $a$}{\abs{E}} + \end{methods} + \textbf{Tipp:} Cut Rekonstruktion mit \code{unionFind} für Partitionierung oder \code{vector<bool>} für edge id's im cut. + \sourcecode{graph/stoerWagner.cpp} +\end{algorithm} + +\subsection{Max-Flow} +\optional{ \subsubsection{Capacity Scaling} -Gut bei dünn besetzten Graphen. -\lstinputlisting{graph/capacityScaling.cpp} +\begin{methods} + \method{maxFlow}{gut bei dünn besetzten Graphen.}{\abs{E}^2\*\log(C)} + \method{addEdge}{fügt eine \textbf{gerichtete} Kante ein}{1} +\end{methods} +\sourcecode{graph/capacityScaling.cpp} +} -% \subsubsection{Push Relabel} -% Gut bei sehr dicht besetzten Graphen. -% \lstinputlisting{graph/pushRelabel.cpp} +\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/pushRelabel3.cpp} \subsubsection{Dinic's Algorithm mit Capacity Scaling} -Nochmal ca. Faktor 2 schneller als Ford Fulkerson mit Capacity Scaling. -\lstinputlisting{graph/dinicScaling.cpp} +\begin{methods} + \method{maxFlow}{doppelt so schnell wie Ford Fulkerson}{\abs{V}^2\cdot\abs{E}} + \method{addEdge}{fügt eine \textbf{gerichtete} Kante ein}{1} +\end{methods} +\sourcecode{graph/dinicScaling.cpp} +\optional{ \subsubsection{Anwendungen} -\begin{itemize}[nosep] +\begin{itemize} \item \textbf{Maximum Edge Disjoint Paths}\newline Finde die maximale Anzahl Pfade von $s$ nach $t$, die keine Kante teilen. - \begin{enumerate}[nosep] + \begin{enumerate} \item Setze $s$ als Quelle, $t$ als Senke und die Kapazität jeder Kante auf 1. \item Der maximale Fluss entspricht den unterschiedlichen Pfaden ohne gemeinsame Kanten. \end{enumerate} \item \textbf{Maximum Independent Paths}\newline Finde die maximale Anzahl an Pfaden von $s$ nach $t$, die keinen Knoten teilen. - \begin{enumerate}[nosep] + \begin{enumerate} \item Setze $s$ als Quelle, $t$ als Senke und die Kapazität jeder Kante \emph{und jedes Knotens} auf 1. \item Der maximale Fluss entspricht den unterschiedlichen Pfaden ohne gemeinsame Knoten. \end{enumerate} @@ -93,26 +195,69 @@ Nochmal ca. Faktor 2 schneller als Ford Fulkerson mit Capacity Scaling. Bei Quelle $s$ und Senke $t$, partitioniere in $S$ und $T$. Zu $S$ gehören alle Knoten, die im Residualgraphen von $s$ aus erreichbar sind (Rückwärtskanten beachten). \end{itemize} +} -\subsection{Min-Cost-Max-Flow} -\lstinputlisting{graph/minCostMaxFlow.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} -\subsection{Maximal Cardinatlity Bipartite Matching}\label{kuhn} -\lstinputlisting{graph/maxCarBiMatch.cpp} -\lstinputlisting{graph/hopcroftKarp.cpp} +\begin{algorithm}{Maximal Cardinatlity Bipartite Matching} + \label{kuhn} + \begin{methods} + \method{kuhn}{berechnet Matching}{\abs{V}\*\min(ans^2, \abs{E})} + \end{methods} + \begin{itemize} + \item die ersten [0..n) Knoten in \code{adjlist} sind die linke Seite des Graphen + \end{itemize} + \sourcecode{graph/maxCarBiMatch.cpp} + \begin{methods} + \method{hopcroft\_karp}{berechnet Matching}{\sqrt{\abs{V}}\*\abs{E}} + \end{methods} + \sourcecode{graph/hopcroftKarp.cpp} +\end{algorithm} -\subsection{Maximum Weight Bipartite Matching} -\lstinputlisting{graph/maxWeightBipartiteMatching.cpp} +\begin{algorithm}{Maximum Weight Bipartite Matching} + \begin{methods} + \method{match}{berechnet Matching}{\abs{V}^3} + \end{methods} + \sourcecode{graph/maxWeightBipartiteMatching.cpp} +\end{algorithm} -\subsection{Wert des maximalen Matchings} -\lstinputlisting{graph/matching.cpp} +\begin{algorithm}{Wert des maximalen Matchings} + Fehlerwahrscheinlichkeit: $\left(\frac{m}{MOD}\right)^I$ + \sourcecode{graph/matching.cpp} +\end{algorithm} -\subsection{2-SAT} -\lstinputlisting{graph/2sat.cpp} +\begin{algorithm}{Allgemeines maximales Matching} + \begin{methods} + \method{match}{berechnet algemeines Matching}{\abs{E}\*\abs{V}\*\log(\abs{V})} + \end{methods} + \sourcecode{graph/blossom.cpp} +\end{algorithm} -% \subsection{TSP} -% \lstinputlisting{graph/TSP.cpp} +\optional{ +\begin{algorithm}{TSP} + \begin{methods} + \method{TSP}{berechnet eine Tour}{n^2\*2^n} + \end{methods} + \sourcecode{graph/TSP.cpp} +\end{algorithm} -\subsection{Bitonic TSP} -\lstinputlisting{graph/bitonicTSP.cpp} +\begin{algorithm}{Bitonic TSP} + \begin{methods} + \method{bitonicTSP}{berechnet eine Bitonische Tour}{n^2} + \end{methods} + \sourcecode{graph/bitonicTSPsimple.cpp} +\end{algorithm} +} +\begin{algorithm}{Maximal Cliques} + \begin{methods} + \method{bronKerbosch}{berechnet alle maximalen Cliquen}{3^\frac{n}{3}} + \method{addEdge}{fügt \textbf{ungerichtete} Kante ein}{1} + \end{methods} + \sourcecode{graph/bronKerbosch.cpp} +\end{algorithm} diff --git a/graph/havelHakimi.cpp b/graph/havelHakimi.cpp new file mode 100644 index 0000000..9fb9846 --- /dev/null +++ b/graph/havelHakimi.cpp @@ -0,0 +1,19 @@ +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}); + vector<vector<int>> adj; + while (!pq.empty()) { + auto v = pq.top(); pq.pop(); + if (sz(pq) < v.first) return {}; //ERROR + vector<pair<int, int>> todo; + for (int i = 0; i < v.first; i++) { + auto u = pq.top(); pq.pop(); + adj[v.second].push_back(u.second); + adj[u.second].push_back(v.second); + u.first--; + if (u.first > 0) todo.push_back(u); + } + for (auto e : todo) pq.push(e); + } + return adj; +} diff --git a/graph/hld.cpp b/graph/hld.cpp new file mode 100644 index 0000000..3d63903 --- /dev/null +++ b/graph/hld.cpp @@ -0,0 +1,52 @@ +vector<vector<int>> adj; +vector<int> sz, in, out, nxt, par; +int t; + +void dfs_sz(int v = 0, int from = -1) { + sz[v] = 1; + for (auto& u : adj[v]) { + if (u != from) { + dfs_sz(u, v); + sz[v] += sz[u]; + } + if (adj[v][0] == from || sz[u] > sz[adj[v][0]]) { + swap(u, adj[v][0]); +}}} + +void dfs_hld(int v = 0, int from = -1) { + par[v] = from; + in[v] = t++; + for (int u : adj[v]) { + if (u == from) continue; + nxt[u] = (u == adj[v][0] ? nxt[v] : u); + dfs_hld(u, v); + } + out[v] = t; +} + +void init() { + int n = sz(adj); + sz.assign(n, 0); in.assign(n, 0); out.assign(n, 0); + nxt.assign(n, 0); par.assign(n, -1); + t = 0; + dfs_sz(); dfs_hld(); +} + +vector<pair<int, int>> get_intervals(int u, int v) { + vector<pair<int, int>> res; + while (true) { + if (in[v] < in[u]) swap(u, v); + if (in[nxt[v]] <= in[u]) { + res.eb(in[u], in[v] + 1); + return res; + } + res.eb(in[nxt[v]], in[v] + 1); + v = par[nxt[v]]; +}} + +int get_lca(int u, int v) { + while (true) { + if (in[v] < in[u]) swap(u, v); + if (in[nxt[v]] <= in[u]) return in[u]; + v = par[nxt[v]]; +}} diff --git a/graph/hopcroftKarp.cpp b/graph/hopcroftKarp.cpp index 629ebf7..132b249 100644 --- a/graph/hopcroftKarp.cpp +++ b/graph/hopcroftKarp.cpp @@ -1,46 +1,43 @@ -// Laufzeit: O(sqrt(|V|)*|E|) -// Kanten von links nach rechts. -// 0: dummy Knoten, 1..n: linke Knoten, n+1..n+m: rechte Knoten vector<vector<int>> adjlist; -vector<int> match, dist; +// pairs ist der gematchte Knoten oder -1 +vector<int> pairs, dist; bool bfs(int n) { - queue<int> q; - dist[0] = INF; - for(int i = 1; i <= n; i++) { - if(match[i] == 0) { dist[i] = 0; q.push(i); } - else dist[i] = INF; - } - while(!q.empty()) { - int u = q.front(); q.pop(); - if(dist[u] < dist[0]) for (int v : adjlist[u]) - if(dist[match[v]] == INF) { - dist[match[v]] = dist[u] + 1; - q.push(match[v]); - } - } - return dist[0] != INF; + queue<int> q; + for(int i = 0; i < n; i++) { + if (pairs[i] < 0) {dist[i] = 0; q.push(i);} + else dist[i] = -1; + } + while(!q.empty()) { + int u = q.front(); q.pop(); + for (int v : adjlist[u]) { + if (pairs[v] < 0) return true; + if (dist[pairs[v]] < 0) { + dist[pairs[v]] = dist[u] + 1; + q.push(pairs[v]); + }}} + return false; } bool dfs(int u) { - if(u != 0) { - for (int v : adjlist[u]) - if(dist[match[v]] == dist[u] + 1) - if(dfs(match[v])) { match[v] = u; match[u] = v; return true; } - dist[u] = INF; - return false; - } - return true; + for (int v : adjlist[u]) { + if (pairs[v] < 0 || + (dist[pairs[v]] > dist[u] && dfs(pairs[v]))) { + pairs[v] = u; pairs[u] = v; + return true; + }} + dist[u] = -1; + return false; } int hopcroft_karp(int n) { // n = #Knoten links - int ans = 0; - match.assign(adjlist.size(), 0); - dist.resize(adjlist.size()); - // Greedy Matching, optionale Beschleunigung. - for (int i = 1; i <= n; i++) for (int w : adjlist[i]) - if (match[w] == 0) { match[i] = w; match[w] = i; ans++; break; } - while(bfs(n)) for(int i = 1; i <= n; i++) - if(match[i] == 0 && dfs(i)) ans++; - return ans; -} + int ans = 0; + pairs.assign(sz(adjlist), -1); + dist.resize(n); + // Greedy Matching, optionale Beschleunigung. + for (int i = 0; i < n; i++) for (int w : adjlist[i]) + if (pairs[w] < 0) {pairs[i] = w; pairs[w] = i; ans++; break;} + while(bfs(n)) for(int i = 0; i < n; i++) + if (pairs[i] < 0) ans += dfs(i); + return ans; +}
\ No newline at end of file diff --git a/graph/kruskal.cpp b/graph/kruskal.cpp new file mode 100644 index 0000000..af5a8ff --- /dev/null +++ b/graph/kruskal.cpp @@ -0,0 +1,9 @@ +sort(edges.begin(), edges.end()); +vector<edge> mst; +int cost = 0; +for (edge& e : edges) { + if (findSet(e.from) != findSet(e.to)) { + unionSets(e.from, e.to); + mst.push_back(e); + cost += e.cost; +}} diff --git a/graph/lca.cpp b/graph/lca.cpp deleted file mode 100644 index d6548e9..0000000 --- a/graph/lca.cpp +++ /dev/null @@ -1,28 +0,0 @@ -struct LCA { - vector<int> depth, visited, first; - int idx; - SparseTable st; - - void init(vector<vector<int>> &g, int root) { // Laufzeit: O(|V|) - depth.assign(2 * g.size(), 0); - visited.assign(2 * g.size(), -1); - first.assign(g.size(), 2 * g.size()); - idx = 0; - visit(g, root, 0); - st.init(&depth); - } - - void visit(vector<vector<int>> &g, int v, int d) { - visited[idx] = v, depth[idx] = d, first[v] = min(idx, first[v]), idx++; - - for (int w : g[v]) { - if (first[w] == 2 * (int)g.size()) { - visit(g, w, d + 1); - visited[idx] = v, depth[idx] = d, idx++; - }}} - - int getLCA(int a, int b) { // Laufzeit: O(1) - if (first[a] > first[b]) swap(a, b); - return visited[st.queryIdempotent(first[a], first[b])]; - } -}; diff --git a/graph/matching.cpp b/graph/matching.cpp index 4383330..ed9ba62 100644 --- a/graph/matching.cpp +++ b/graph/matching.cpp @@ -1,35 +1,41 @@ -// Fehlerwahrscheinlichkeit: (n / MOD)^I -const int N=200, MOD=1000000007, I=10; -int n, adj[N][N], a[N][N]; +constexpr int MOD=1000000007, I=10; +vector<vector<ll>> adjlist, mat; -int rank() { - int r = 0; - for (int j = 0; j < n; j++) { - int k = r; - while (k < n && !a[k][j]) ++k; - if (k == n) continue; - swap(a[r], a[k]); - int inv = powmod(a[r][j], MOD - 2); - for (int i = j; i < n; i++) - a[r][i] = 1LL * a[r][i] * inv % MOD; - for (int u = r + 1; u < n; u++) - for (int v = j; v < n; v++) - a[u][v] = (a[u][v] - 1LL * a[r][v] * a[u][j] % MOD + MOD) % MOD; - ++r; - } - return r; +int gauss(int n, ll p) { + int rank = n; + for (int line = 0; line < n; line++) { + int swappee = line; + while (swappee < n && mat[swappee][line] == 0) swappee++; + if (swappee == n) {rank--; continue;} + swap(mat[line], mat[swappee]); + ll factor = powMod(mat[line][line], p - 2, p); + for (int i = 0; i < n; i++) { + mat[line][i] *= factor; + mat[line][i] %= p; + } + for (int i = 0; i < n; i++) { + if (i == line) continue; + ll diff = mat[i][line]; + for (int j = 0; j < n; j++) { + mat[i][j] -= (diff * mat[line][j]) % p; + mat[i][j] %= p; + if (mat[i][j] < 0) mat[i][j] += p; + }}} + return rank; } int max_matching() { - int ans = 0; - for (int _ = 0; _ < I; _++) { - for (int i = 0; i < n; i++) { - for (int j = 0; j < i; j++) { - if (adj[i][j]) { - a[i][j] = rand() % (MOD - 1) + 1; - a[j][i] = MOD - a[i][j]; - }}} - ans = max(ans, rank()/2); - } - return ans; + int ans = 0; + mat.assign(sz(adjlist), vector<ll>(sz(adjlist))); + for (int _ = 0; _ < I; _++) { + for (int i = 0; i < sz(adjlist); i++) { + mat[i].assign(sz(adjlist), 0); + for (int j : adjlist[i]) { + if (j < i) { + mat[i][j] = rand() % (MOD - 1) + 1; + mat[j][i] = MOD - mat[i][j]; + }}} + ans = max(ans, gauss(sz(adjlist), MOD)/2); + } + return ans; } diff --git a/graph/maxCarBiMatch.cpp b/graph/maxCarBiMatch.cpp index 24aebef..0a38d84 100644 --- a/graph/maxCarBiMatch.cpp +++ b/graph/maxCarBiMatch.cpp @@ -1,5 +1,3 @@ -// Laufzeit: O(n*min(ans^2, |E|)) -// Kanten von links nach rechts. Die ersten n Knoten sind links, die anderen rechts. vector<vector<int>> adjlist; vector<int> pairs; // Der gematchte Knoten oder -1. vector<bool> visited; @@ -14,12 +12,12 @@ bool dfs(int v) { } int kuhn(int n) { // n = #Knoten links. - pairs.assign(adjlist.size(), -1); + pairs.assign(sz(adjlist), -1); int ans = 0; // Greedy Matching. Optionale Beschleunigung. for (int i = 0; i < n; i++) for (auto w : adjlist[i]) - if (pairs[w] == -1) { pairs[i] = w; pairs[w] = i; ans++; break; } - for (int i = 0; i < n; i++) if (pairs[i] == -1) { + if (pairs[w] < 0) {pairs[i] = w; pairs[w] = i; ans++; break;} + for (int i = 0; i < n; i++) if (pairs[i] < 0) { visited.assign(n, false); ans += dfs(i); } diff --git a/graph/maxWeightBipartiteMatching.cpp b/graph/maxWeightBipartiteMatching.cpp index f734fa4..ef99232 100644 --- a/graph/maxWeightBipartiteMatching.cpp +++ b/graph/maxWeightBipartiteMatching.cpp @@ -1,54 +1,59 @@ -// Laufzeit: O(|V|^3) -int costs[N_LEFT][N_RIGHT]; +double costs[N_LEFT][N_RIGHT]; -// Es muss l<=r sein, ansonsten terminiert der Algorithmus nicht. -int match(int l, int r) { - vector<int> xy(l, -1), yx(r, -1), lx(l), ly(r, 0), augmenting(r); - vector<bool> s(l); - vector<ii> slack(r, ii(0,0)); +// Es muss l<=r sein! (sonst Endlosschleife) +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<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++) { - fill(augmenting.begin(), augmenting.end(), -1); - fill(s.begin(), s.end(), false); - s[root] = true; - for (int y = 0; y < r; y++) { - slack[y] = ii(lx[root] + ly[y] - costs[root][y], root); - } - int y = -1; - for (;;) { - int delta = INT_MAX, x = -1; - for (int yy = 0; yy < r; yy++) { - if (augmenting[yy] == -1) { - if (slack[yy].first < delta) { - delta = slack[yy].first; - x = slack[yy].second; - 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] > -1) ly[y] += delta; - else slack[y].first -= delta; - }} - augmenting[y] = x; - x = yx[y]; - if (x == -1) break; - s[x] = true; - for (int y = 0; y < r; y++) { - if (augmenting[y] == -1) { - ii alt = ii(lx[x] + ly[y] - costs[x][y], x); - if (slack[y].first > alt.first) { - slack[y] = alt; - }}}} - while (y != -1) { - // 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; - }} - return accumulate(lx.begin(), lx.end(), 0) + - accumulate(ly.begin(), ly.end(), 0); // Wert des Matchings. + 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); + s[root] = true; + for (int y = 0; y < r; y++) { + slack[y] = {lx[root] + ly[y] - costs[root][y], root}; + } + int y = -1; + while (true) { + 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 (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; + else slack[y].first -= delta; + }} + augmenting[y] = x; + x = yx[y]; + if (x == -1) break; + s[x] = true; + for (int y = 0; y < r; y++) { + if (augmenting[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; + }} + // Wert des Matchings + return accumulate(lx.begin(), lx.end(), 0.0) + + accumulate(ly.begin(), ly.end(), 0.0); } diff --git a/graph/minCostMaxFlow.cpp b/graph/minCostMaxFlow.cpp index 8d3ca4c..ee8aa10 100644 --- a/graph/minCostMaxFlow.cpp +++ b/graph/minCostMaxFlow.cpp @@ -1,67 +1,64 @@ -static const ll flowlimit = 1LL << 60; // Größer als der maximale Fluss. -struct MinCostFlow { // Mit new erstellen! - static const int maxn = 400; // Größer als die Anzahl der Knoten. - static const int maxm = 5000; // Größer als die Anzahhl der Kanten. - struct edge { int node, next; ll flow, value; } edges[maxm << 1]; - int graph[maxn], queue[maxn], pre[maxn], con[maxn]; - int n, m, source, target, top; - bool inqueue[maxn]; - ll maxflow, mincost, dis[maxn]; +constexpr ll INF = 1LL << 60; // Größer als der maximale Fluss. +struct MinCostFlow { + struct edge { + int to; + ll f, cost; + }; + vector<edge> edges; + vector<vector<int>> adjlist; + vector<int> pref, con; + vector<ll> dist; - MinCostFlow() { memset(graph, -1, sizeof(graph)); top = 0; } + const int s, t; + ll maxflow, mincost; - inline int inverse(int x) { return 1 + ((x >> 1) << 2) - x; } + MinCostFlow(int n, int source, int target) : + adjlist(n), s(source), t(target) {}; - // Directed edge from u to v, capacity c, weight w. - inline int addedge(int u, int v, int c, int w) { - edges[top].value = w; edges[top].flow = c; edges[top].node = v; - edges[top].next = graph[u]; graph[u] = top++; - edges[top].value = -w; edges[top].flow = 0; edges[top].node = u; - edges[top].next = graph[v]; graph[v] = top++; - return top - 2; + void addedge(int u, int v, ll c, ll cost) { + adjlist[u].push_back(sz(edges)); + edges.push_back({v, c, cost}); + adjlist[v].push_back(sz(edges)); + edges.push_back({u, 0, -cost}); } bool SPFA() { - int point, node, now, head = 0, tail = 1; - memset(pre, -1, sizeof(pre)); - memset(inqueue, 0, sizeof(inqueue)); - memset(dis, 0x7F, sizeof(dis)); - dis[source] = 0; queue[0] = source; - pre[source] = source; inqueue[source] = true; + pref.assign(sz(adjlist), - 1); + dist.assign(sz(adjlist), INF); + vector<bool> inqueue(sz(adjlist)); + queue<int> queue; - while (head != tail) { - now = queue[head++]; - point = graph[now]; - inqueue[now] = false; - head %= maxn; + dist[s] = 0; queue.push(s); + pref[s] = s; inqueue[s] = true; - while (point != -1) { - node = edges[point].node; - if (edges[point].flow > 0 && - dis[node] > dis[now] + edges[point].value) { - dis[node] = dis[now] + edges[point].value; - pre[node] = now; con[node] = point; - if (!inqueue[node]) { - inqueue[node] = true; queue[tail++] = node; - tail %= maxn; - }} - point = edges[point].next; - }} - return pre[target] != -1; + while (!queue.empty()) { + int cur = queue.front(); queue.pop(); + inqueue[cur] = false; + for (int id : adjlist[cur]) { + int to = edges[id].to; + if (edges[id].f > 0 && + dist[to] > dist[cur] + edges[id].cost) { + dist[to] = dist[cur] + edges[id].cost; + pref[to] = cur; con[to] = id; + if (!inqueue[to]) { + inqueue[to] = true; queue.push(to); + }}}} + return pref[t] != -1; } void extend() { - ll w = flowlimit; - for (int u = target; pre[u] != u; u = pre[u]) - w = min(w, edges[con[u]].flow); + ll w = INF; + for (int u = t; pref[u] != u; u = pref[u]) + w = min(w, edges[con[u]].f); maxflow += w; - mincost += dis[target] * w; - for (int u = target; pre[u] != u; u = pre[u]) { - edges[con[u]].flow -= w; - edges[inverse(con[u])].flow += w; + mincost += dist[t] * w; + for (int u = t; pref[u] != u; u = pref[u]) { + edges[con[u]].f -= w; + edges[con[u] ^ 1].f += w; }} void mincostflow() { + con.assign(sz(adjlist), 0); maxflow = mincost = 0; while (SPFA()) extend(); } diff --git a/graph/pushRelabel.cpp b/graph/pushRelabel.cpp index 7bb1145..182fa12 100644 --- a/graph/pushRelabel.cpp +++ b/graph/pushRelabel.cpp @@ -1,28 +1,29 @@ -// Laufzeit: O(|V|^3) struct PushRelabel { - ll capacities[MAX_V][MAX_V], flow[MAX_V][MAX_V], excess[MAX_V]; - int height[MAX_V], list[MAX_V - 2], seen[MAX_V], n; + vector<vector<long long>> capacitie, flow; + vector<long long> excess; + vector<int> height, seen, list; + int n; PushRelabel(int n) { this->n = n; - memset(capacities, 0L, sizeof(capacities)); - memset(flow, 0L, sizeof(flow)); - memset(excess, 0L, sizeof(excess)); - memset(height, 0, sizeof(height)); - memset(list, 0, sizeof(list)); - memset(seen, 0, sizeof(seen)); + capacities.assign(n, vector<long long>(n)); + flow.assign(n, vector<long long>(n)); + excess.assign(n, 0); + height.assign(n, 0); + seen.assign(n, 0); + list.assign(n - 2, 0); } - inline void addEdge(int u, int v, ll c) { capacities[u][v] += c; } + inline void addEdge(int u, int v, long long c) {capacities[u][v] += c;} void push(int u, int v) { - ll send = min(excess[u], capacities[u][v] - flow[u][v]); + long long send = min(excess[u], capacities[u][v] - flow[u][v]); flow[u][v] += send; flow[v][u] -= send; excess[u] -= send; excess[v] += send; } void relabel(int u) { - int minHeight = INT_MAX / 2; + int minHeight = INT_INF; for (int v = 0; v < n; v++) { if (capacities[u][v] - flow[u][v] > 0) { minHeight = min(minHeight, height[v]); @@ -47,12 +48,12 @@ struct PushRelabel { list[0] = temp; } - ll maxFlow(int source, int target) { + long long maxFlow(int source, int target) { for (int i = 0, p = 0; i < n; i++) if (i != source && i != target) list[p++] = i; height[source] = n; - excess[source] = LLONG_MAX / 2; + excess[source] = INF; for (int i = 0; i < n; i++) push(source, i); int p = 0; @@ -65,7 +66,7 @@ struct PushRelabel { } else p++; } - ll maxflow = 0L; + long long maxflow = 0; for (int i = 0; i < n; i++) maxflow += flow[source][i]; return maxflow; } diff --git a/graph/pushRelabel2.cpp b/graph/pushRelabel2.cpp new file mode 100644 index 0000000..8b5f0c6 --- /dev/null +++ b/graph/pushRelabel2.cpp @@ -0,0 +1,109 @@ +constexpr ll inf = 1ll<<60; + +struct edge { + int from, to; + ll f, c; +}; + +vector<edge> edges; +vector<vector<int>> adjlist, 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) { + adjlist[from].push_back(edges.size()); + edges.push_back({from, to, 0, c}); + adjlist[to].push_back(edges.size()); + edges.push_back({to, from, 0, 0}); +} + +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 u = que[qh++], h = height[u] + 1; + for (int id : adjlist[u]) { + 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 u = 0; u < n; u++) { + if (height[u] < n) { + iter[u] = dlist[height[u]].insert(dlist[height[u]].begin(), u); + if (excess[u] > 0) llist[height[u]].push_back(u); + }} + highest = highestActive = height[que[qt - 1]]; +} + +void push(int u, int id) { + int v = edges[id].to; + ll df = min(excess[u], edges[id].c - edges[id].f); + edges[id].f += df; + edges[id^1].f -= df; + excess[u] -= df; + excess[v] += df; + if (0 < excess[v] && excess[v] <= df) llist[height[v]].push_back(v); +} + +void discharge(int n, int u) { + int nh = n; + for (int id : adjlist[u]) { + if (edges[id].c - edges[id].f > 0) { + if (height[u] == height[edges[id].to] + 1) { + push(u, id); + if (!excess[u]) return; + } else { + nh = min(nh, height[edges[id].to] + 1); + }}} + int h = height[u]; + 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[u] = dlist[h].erase(iter[u]), height[u] = nh; + if (nh == n) return; + ++ccount[nh], iter[u] = dlist[nh].insert(dlist[nh].begin(), u); + highest = max(highest, highestActive = nh); + llist[nh].push_back(u); +}} + +ll maxFlow(int s, int t) { + int n = adjlist.size(); + llist.assign(n + 1, {}); + dlist.assign(n + 1, {}); + highestActive = highest = 0; + height.assign(n, 0); + height[s] = n; + iter.resize(n); + for (int i = 0; i < n; i++) { + if (i != s) iter[i] = dlist[height[i]].insert(dlist[height[i]].begin(), i); + } + ccount.assign(n, 0); + ccount[0] = n-1; + excess.assign(n, 0); + excess[s] = inf; + excess[t] = -inf; + for (int id : adjlist[s]) push(s, id); + globalRelabel(n, t); + while (highestActive >= 0) { + if (llist[highestActive].empty()) { + highestActive--; + continue; + } + int u = llist[highestActive].back(); + llist[highestActive].pop_back(); + discharge(n, u); + } + return excess[t] + inf; +}
\ No newline at end of file diff --git a/graph/pushRelabel3.cpp b/graph/pushRelabel3.cpp new file mode 100644 index 0000000..d4d2e67 --- /dev/null +++ b/graph/pushRelabel3.cpp @@ -0,0 +1,66 @@ +struct edge { + int from, to; + ll f, c; +}; + +vector<edge> edges; +vector<vector<int>> adjlist, hs; +vector<ll> ec; +vector<int> cur, H; + +void addEdge(int from, int to, ll c) { + adjlist[from].push_back(sz(edges)); + edges.push_back({from, to, 0, c}); + adjlist[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(adjlist); + 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 : adjlist[s]) addFlow(id, edges[id].c); + for (int hi = 0;;) { + while (hs[hi].empty()) if (!hi--) return -ec[s]; + int u = hs[hi].back(); + hs[hi].pop_back(); + while (ec[u] > 0) { + if (cur[u] == sz(adjlist[u])) { + H[u] = 2*n; + for (int i = 0; i < sz(adjlist[u]); i++) { + int id = adjlist[u][i]; + if (edges[id].c - edges[id].f > 0 && + H[u] > H[edges[id].to] + 1) { + H[u] = H[edges[id].to] + 1; + cur[u] = i; + }} + co[H[u]]++; + 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[u]; + } else { + auto e = edges[adjlist[u][cur[u]]]; + if (e.c - e.f > 0 && H[u] == H[e.to] + 1) { + addFlow(adjlist[u][cur[u]], min(ec[u], e.c - e.f)); + } else { + cur[u]++; +}}}}} diff --git a/graph/scc.cpp b/graph/scc.cpp index 9eb0881..b21d544 100644 --- a/graph/scc.cpp +++ b/graph/scc.cpp @@ -1,17 +1,17 @@ -// Laufzeit: O(|V|+|E|) +vector<vector<int>> adjlist; + int counter, sccCounter; -vector<bool> visited, inStack; -vector< vector<int> > adjlist; -vector<int> d, low, sccs; // sccs enthält den Index der SCC pro Knoten. -stack<int> s; +vector<bool> inStack; +vector<vector<int>> sccs; +// idx enthält den Index der SCC pro Knoten. +vector<int> d, low, idx, s; void visit(int v) { - visited[v] = true; d[v] = low[v] = counter++; - s.push(v); inStack[v] = true; + s.push_back(v); inStack[v] = true; for (auto u : adjlist[v]) { - if (!visited[u]) { + if (d[u] < 0) { visit(u); low[v] = min(low[v], low[u]); } else if (inStack[u]) { @@ -19,23 +19,23 @@ void visit(int v) { }} if (d[v] == low[v]) { + sccs.push_back({}); int u; do { - u = s.top(); s.pop(); inStack[u] = false; - sccs[u] = sccCounter; + u = s.back(); s.pop_back(); inStack[u] = false; + idx[u] = sccCounter; + sccs[sccCounter].push_back(u); } while (u != v); sccCounter++; }} void scc() { - visited.assign(adjlist.size(), false); - d.assign(adjlist.size(), -1); - low.assign(adjlist.size(), -1); - inStack.assign(adjlist.size(), false); - sccs.resize(adjlist.size(), -1); + inStack.assign(sz(adjlist), false); + d.assign(sz(adjlist), -1); + low.assign(sz(adjlist), -1); + idx.assign(sz(adjlist), -1); counter = sccCounter = 0; - for (int i = 0; i < (int)adjlist.size(); i++) { - if (!visited[i]) { - visit(i); -}}} + for (int i = 0; i < sz(adjlist); i++) { + if (d[i] < 0) visit(i); +}} diff --git a/graph/stoerWagner.cpp b/graph/stoerWagner.cpp new file mode 100644 index 0000000..899cb3b --- /dev/null +++ b/graph/stoerWagner.cpp @@ -0,0 +1,53 @@ +struct edge { + int from, to; + ll cap; +}; + +vector<vector<edge>> adjlist, tmp; +vector<bool> erased; + +void merge(int a, int b) { + tmp[a].insert(tmp[a].end(), all(tmp[b])); + tmp[b].clear(); + erased[b] = true; + for (auto& v : tmp) { + for (auto&e : v) { + if (e.from == b) e.from = a; + if (e.to == b) e.to = a; +}}} + +ll stoer_wagner() { + ll res = INF; + tmp = adjlist; + erased.assign(sz(tmp), false); + for (int i = 1; i < sz(tmp); i++) { + int s = 0; + while (erased[s]) s++; + priority_queue<pair<ll, int>> pq; + pq.push({0, s}); + vector<ll> con(sz(tmp)); + ll cur = 0; + vector<pair<ll, int>> state; + while (!pq.empty()) { + int c = pq.top().second; + pq.pop(); + if (con[c] < 0) continue; //already seen + con[c] = -1; + for (auto e : tmp[c]) { + if (con[e.to] >= 0) {//add edge to cut + con[e.to] += e.cap; + pq.push({con[e.to], e.to}); + cur += e.cap; + } else if (e.to != c) {//remove edge from cut + cur -= e.cap; + }} + state.push_back({cur, c}); + } + int t = state.back().second; + state.pop_back(); + if (state.empty()) return 0; //graph is not connected?! + merge(state.back().second, t); + res = min(res, state.back().first); + } + return res; +} diff --git a/graph/treeIsomorphism.cpp b/graph/treeIsomorphism.cpp new file mode 100644 index 0000000..f3e147b --- /dev/null +++ b/graph/treeIsomorphism.cpp @@ -0,0 +1,41 @@ +vector<vector<vector<int>>> +getTreeLabel(const vector<vector<int>>& adj, int root) { + vector<vector<int>> level = {{root}}; + vector<int> dist(sz(adj), -1); + dist[root] = 0; + queue<int> q; + q.push(root); + while (!q.empty()) { + int c = q.front(); + q.pop(); + for (int n : adj[c]) { + if (dist[n] < 0) { + dist[n] = dist[c] + 1; + if (sz(level) <= dist[n]) level.push_back({}); + level[dist[n]].push_back(n); + q.push(n); + }}} + + vector<vector<vector<int>>> levelLabels(sz(level)); + vector<int> shortLabel(sz(adj)); + for (int l = sz(level) - 1; l >= 0; l--) { + vector<pair<vector<int>, int>> tmpLabels; + for (int v : level[l]) { + vector<int> label; + for (int n : adj[v]) { + if (dist[n] > dist[v]) { + label.push_back(shortLabel[n]); + }} + sort(all(label)); + tmpLabels.push_back({label, v}); + } + sort(all(tmpLabels)); + vector<int>& last = tmpLabels[0].first; + int curId = 0; + for (auto& e : tmpLabels) { + levelLabels[l].push_back(e.first); + if (e.first != last) curId++, last = e.first; + shortLabel[e.second] = curId; + }} + return levelLabels; +} diff --git a/java/bigInteger.java b/java/bigInteger.java new file mode 100644 index 0000000..28490bf --- /dev/null +++ b/java/bigInteger.java @@ -0,0 +1,25 @@ +// Berechnet this +,*,/,- val. +BigInteger add(BigInteger val), multiply(BigInteger val), + divide(BigInteger val), substract(BigInteger val) + +// Berechnet this^e. +BigInteger pow(BigInteger e) + +// Bit-Operationen. +BigInteger and(BigInteger val), or(BigInteger val), xor(BigInteger val), + not(), shiftLeft(int n), shiftRight(int n) + +// Berechnet den ggT von abs(this) und abs(val). +BigInteger gcd(BigInteger val) + +// Berechnet this mod m, this^-1 mod m, this^e mod m. +BigInteger mod(BigInteger m), modInverse(BigInteger m), + modPow(BigInteger e, BigInteger m) + +// Berechnet die nächste Zahl, die größer und wahrscheinlich prim ist. +BigInteger nextProbablePrime() + +// Berechnet int/long/float/double-Wert. +// Ist die Zahl zu großen werden die niedrigsten Bits konvertiert. +int intValue(), long longValue(), +float floatValue(), double doubleValue()
\ No newline at end of file diff --git a/java/inputA.java b/java/inputA.java new file mode 100644 index 0000000..6616c89 --- /dev/null +++ b/java/inputA.java @@ -0,0 +1,4 @@ +Scanner in = new Scanner(System.in); // java.util.Scanner +String line = in.nextLine(); // Die nächste Zeile. +int num1 = in.nextInt(); // Das nächste Token als int. +double num2 = in.nextDouble(); // Das nächste Token als double.
\ No newline at end of file diff --git a/java/inputB.java b/java/inputB.java new file mode 100644 index 0000000..9175d56 --- /dev/null +++ b/java/inputB.java @@ -0,0 +1,7 @@ +BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); +String line = in.readLine(); // Die nächste Zeile. +String[] parts = line.split(" "); // Zeile in Tokens aufspalten. +int num1 = Integer.parseInt(in.readLine); +int num2 = Integer.parseInt(parts[0]); +double num3 = Double.parseDouble(in.readLine); +double num4 = Double.parseDouble(parts[0]);
\ No newline at end of file diff --git a/java/java.tex b/java/java.tex new file mode 100644 index 0000000..af8a024 --- /dev/null +++ b/java/java.tex @@ -0,0 +1,37 @@ +\section{Java} +\lstset{language=Java} + +\optional{ +\subsection{Introduction} + +\begin{itemize} + \item Compilen: \code{javac main.java} + \item Ausführen: \code{java main < sample.in} +\end{itemize} +} + +\subsection{Input} +\begin{itemize} + \item \code{Scanner} ist sehr langsam. Nicht für lange Eingaben verwenden +\end{itemize} +\optional{ +\lstinputlisting{java/inputA.java} +\lstinputlisting{java/inputB.java} +} + +\subsection{Output} +\begin{itemize} + \item \code{System.out} flusht nach jeder newline $\Rightarrow$ langsam + \item \code{String.format} langsam + \item \code{+} auf \code{String} benutzt \code{StringBuilder} $\Rightarrow$ schnell und leicht \\(bei vielen \code{+}-Operationen an unterschiedlichen Stellen doch explizit \code{StringBuilder} nutzen) +\end{itemize} +\optional{ +\lstinputlisting{java/output.java} +} + +\optional{ +\subsection{BigInteger} +\lstinputlisting{java/bigInteger.java} +} + +\lstset{language=C++} diff --git a/java/output.java b/java/output.java new file mode 100644 index 0000000..a068d8d --- /dev/null +++ b/java/output.java @@ -0,0 +1,7 @@ +//gepufferter nicht autoatischer flushender output +PrintWriter out = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(new FileOutputStream( + FileDescriptor.out), StandardCharsets.UTF_8), 4096)); +out.println("blub" + "a" + "b"); //zeile Ausgeben +out.println(String.format("%d %s", 5, "a")) // wie printf +out.close();//WICHTIG!
\ No newline at end of file diff --git a/latexHeaders/commands.sty b/latexHeaders/commands.sty new file mode 100644 index 0000000..abe589d --- /dev/null +++ b/latexHeaders/commands.sty @@ -0,0 +1,49 @@ +% custom commands +\newcommand{\optional}[1]{ + \ifoptional + #1 + \fi} +\newcommand{\runtime}[1]{\ensuremath{\mathcal{O}\left(#1\right)}} +\newcommand{\code}[1]{\lstinline[breaklines=true]{#1}} + +\usepackage{tikz} + + +%new environment to define algorithms +\usepackage{ifthen} +\NewDocumentEnvironment{algorithm}{ O{required} m +b }{}{ + \ifthenelse{\equal{#1}{optional}}{% + \optional{ + \needspace{4\baselineskip}% + \subsection{#2\textcolor{gray}{(optional)}}% + #3% + } + }{% + \needspace{4\baselineskip}% + \subsection{#2}% + #3% + } +} + +%\ifthenelse{\equal{#3}{}}{}{\runtime{#3}} + +\newcommand{\sourcecode}[1]{% + \nobreak% +% \needspace{3\baselineskip}% +% \nopagebreak% + \lstinputlisting{#1}% + \penalty -1000% +} +\newcommand{\method}[4][]{\texttt{#2}~~#3~~\runtime{#4}#1\par} + +\newenvironment{methods}[1][lll]{% + %\begin{minipage}{\linewidth}% + \renewcommand{\method}[4][]{\texttt{##2}&##3&\ifthenelse{\equal{##4}{}}{}{\runtime{##4}}##1\\}% + \begin{tabular}{@{}#1@{}}% +}{% + \end{tabular}% + %\end{minipage}% + \nobreak% + \needspace{3\baselineskip}% + \nobreak% +}
\ No newline at end of file diff --git a/latexHeaders/layout.sty b/latexHeaders/layout.sty new file mode 100644 index 0000000..1c8dc00 --- /dev/null +++ b/latexHeaders/layout.sty @@ -0,0 +1,81 @@ +% Don't waste space at the page borders. Use two column layout. +\usepackage[ + top=2cm, + bottom=1cm, + left=1cm, + right=1cm, + landscape +]{geometry} + +% Headline and bottomline. +\usepackage{scrlayer-scrpage} +\pagestyle{scrheadings} +\clearscrheadfoot +\ihead{\university} +\chead{\teamname} +\ohead{\pagemark} + +% Shift the title up to waste less space. +\usepackage{titling} +\setlength{\droptitle}{-8em} + +% Multicol layout for the table of contents. +\usepackage{multicol} +\usepackage{multirow} +\usepackage{array} + +% Automatically have table fill horizontal space. +\usepackage{makecell} +\usepackage{tabularx} +\newcolumntype{C}{>{\centering\arraybackslash}X} +\newcolumntype{L}{>{\raggedright\arraybackslash}X} +\newcolumntype{R}{>{\raggedleft\arraybackslash}X} +\newcolumntype{I}{!{\color{lightgray}\vrule}} +\usepackage{colortbl} +\newcommand{\grayhline}{\arrayrulecolor{lightgray}\hline + \arrayrulecolor{black}} + +% Nice table line. +\usepackage{booktabs} + +% Dingbats symbols. +\usepackage{pifont} + +% use less space... +%\usepackage[subtle, sections, indent, leading, charwidths]{savetrees} +\usepackage[moderate,sections]{savetrees} +\RedeclareSectionCommands[ + beforeskip=1pt plus 5pt, + afterskip=0.1pt plus 1.5pt +]{section,subsection,subsubsection} +\RedeclareSectionCommands[ + beforeskip=1pt plus 5pt, + afterskip=-1.2ex +]{paragraph} + +% dont indent paragagraphs +\setlength{\parindent}{0em} +\parskip=0pt + +% dont encourage breaks before lists +\@beginparpenalty=10000 + +% Nice enumerations without wasting space above and below. +\usepackage{relsize} +\usepackage{enumitem} +\setlist{nosep,leftmargin=2ex,labelwidth=1ex,labelsep=1ex} +\setlist[2]{leftmargin=3ex,label=\smaller[2]\ding{228}} +\setlist[3]{leftmargin=3ex,label=\larger\textbf{--}} +\setlist[description]{leftmargin=0pt} + +% decrease space for tables +\tabcolsep=2pt +\setlength\extrarowheight{0.3pt plus 1pt} + +\newenvironment{expandtable}{% + \begin{addmargin}{-3.4pt} +}{% + \end{addmargin} +} + +\usepackage{needspace} diff --git a/latexHeaders/layout.tex b/latexHeaders/layout.tex deleted file mode 100644 index 843798a..0000000 --- a/latexHeaders/layout.tex +++ /dev/null @@ -1,43 +0,0 @@ -% Don't waste space at the page borders. Use two column layout. -\usepackage[ - top=2cm, - bottom=1cm, - left=1cm, - right=1cm, - landscape -]{geometry} - - -% Headline and bottomline. -\usepackage{scrpage2} -\pagestyle{scrheadings} -\clearscrheadfoot -\ihead{\university} -\chead{\teamname} -\ohead{\pagemark} - -% Shift the title up to waste less space. -\usepackage{titling} -\setlength{\droptitle}{-9em} - -% Reduce spaces around sections and subsections. -\usepackage{titlesec} -\titlespacing*{\section}{0pt}{0pt}{0pt} -\titlespacing*{\subsection}{0pt}{0pt}{0pt} - -% Nice enumerations without wasting space above and below. -\usepackage{enumitem} -\setlist{nosep} - -% Multicol layout for the table of contents. -\usepackage{multicol} -\usepackage{multirow} - -% Automatically have table fill horizontal space. -\usepackage{tabularx} - -% Nice table line. -\usepackage{booktabs} - -% Dingbats symbols. -\usepackage{pifont} diff --git a/latexHeaders/listings.sty b/latexHeaders/listings.sty new file mode 100644 index 0000000..d1aa5f0 --- /dev/null +++ b/latexHeaders/listings.sty @@ -0,0 +1,106 @@ +% Colors, used for syntax highlighting. +% To print this document, set all colors to black! +\usepackage{xcolor} +\definecolor{safeRed}{HTML}{D7191C} +\definecolor{safeOrange}{HTML}{FFDE71} +\definecolor{safeYellow}{HTML}{FFFFBF} +\definecolor{safeGreen}{HTML}{99CF8F} +\definecolor{safeBlue}{HTML}{2B83BA} + +%try printer friendly colors? +%\colorlet{keyword}{safeBlue} +%\colorlet{string}{safeRed} +%\colorlet{comment}{safeGreen} +%\colorlet{identifier}{black} +\definecolor{keyword}{HTML}{2750A0} +\definecolor{string}{HTML}{7B3294} +\definecolor{comment}{HTML}{1A9641} +\definecolor{identifier}{HTML}{000000} + +% Source code listings. +\usepackage[scaled=0.80]{beramono} + +\usepackage{listings} +\lstset{ + language={C++}, + numbers=left, + stepnumber=1, + numbersep=6pt, + numberstyle=\small, + breaklines=true, + breakautoindent=true, + breakatwhitespace=false, + numberblanklines=true, + postbreak=\space, + tabsize=2, + basicstyle=\ttfamily\normalsize, + showspaces=false, + showstringspaces=false, + extendedchars=true, + keywordstyle=\color{keyword}\bfseries, + stringstyle=\color{string}\bfseries, + commentstyle=\color{comment}\bfseries\itshape, + identifierstyle=\color{identifier}, + frame=trbl, + aboveskip=3pt, + belowskip=3pt, + escapechar=@ + %moredelim=**[is][{\btHL[fill=green!30,draw=red,dashed,thin]}]{@}{@} +} + +% Listings doesn't support UTF8. This is just enough for German umlauts. +\lstset{literate=% + {Ö}{{\"O}}1 + {Ä}{{\"A}}1 + {Ü}{{\"U}}1 + {ß}{{\ss}}1 + {ü}{{\"u}}1 + {ä}{{\"a}}1 + {ö}{{\"o}}1 + {~}{{\textasciitilde}}1 +} + +\let\orig@lstnumber=\thelstnumber +\newcommand\lstresetnumber{\global\let\thelstnumber=\orig@lstnumber} +\let\orig@placelstnumber=\lst@PlaceNumber +\gdef\lst@PlaceNumber{\orig@placelstnumber\lstresetnumber} +\newcommand\lstsettmpnumber[1]{\gdef\thelstnumber{#1}} + +\lst@AddToHook{OnEmptyLine}{% + \ifnum\value{lstnumber}>99 + \lstsettmpnumber{\_\_\_} + \else\ifnum\value{lstnumber}>9 + \lstsettmpnumber{\_\_} + \else + \lstsettmpnumber{\_} + \fi\fi +% \lstsettmpnumber{\_\_\kern-6pt}% + \vspace{-1.75ex}% + \addtocounter{lstnumber}{-1}% +} +% old: (change numberblanklines=false!) +%\lst@AddToHook{OnEmptyLine}{% +% \vspace{\dimexpr\baselineskip+0.5em}% +% \addtocounter{lstnumber}{-1}% +%} + +\newenvironment{btHighlight}[1][] +{\begingroup\tikzset{bt@Highlight@par/.style={#1}}\begin{lrbox}{\@tempboxa}} +{\end{lrbox}\bt@HL@box[bt@Highlight@par]{\@tempboxa}\endgroup} + +\newcommand\btHL[1][]{% + \begin{btHighlight}[#1]\bgroup\aftergroup\bt@HL@endenv% + } + \def\bt@HL@endenv{% + \end{btHighlight}% + \egroup% +} +\newcommand{\bt@HL@box}[2][]{% + \tikz[#1]{% + \pgfpathrectangle{\pgfpoint{1pt}{0pt}}{\pgfpoint{\wd #2}{\ht #2}}% + \pgfusepath{use as bounding box}% + \node[anchor=base west, fill=orange!30,outer sep=0pt,inner xsep=2.2pt, inner ysep=0pt, rounded corners=3pt, minimum height=\ht\strutbox+1pt,#1]{\raisebox{1pt}{\strut}\strut\usebox{#2}}; + }% +} + +\newcommand{\hl}[1]{\btHL[fill=safeOrange,draw=black,thin]{#1}}
\ No newline at end of file diff --git a/latexHeaders/listings.tex b/latexHeaders/listings.tex index 135b2af..caac6b1 100644 --- a/latexHeaders/listings.tex +++ b/latexHeaders/listings.tex @@ -1,34 +1,51 @@ % Colors, used for syntax highlighting. % To print this document, set all colors to black! \usepackage{xcolor} -\definecolor{keyword}{rgb}{0, 0, 1} -\definecolor{string}{rgb}{1, 0, 0} -\definecolor{comment}{rgb}{0.2, 0.6, 0.2} -\definecolor{identifier}{rgb}{0, 0, 0} +\definecolor{safeRed}{HTML}{D7191C} +\definecolor{safeOrange}{HTML}{FFDE71} +\definecolor{safeYellow}{HTML}{FFFFBF} +\definecolor{safeGreen}{HTML}{99CF8F} +\definecolor{safeBlue}{HTML}{2B83BA} + +%try printer friendly colors? +%\colorlet{keyword}{safeBlue} +%\colorlet{string}{safeRed} +%\colorlet{comment}{safeGreen} +%\colorlet{identifier}{black} +\definecolor{keyword}{HTML}{2750A0} +\definecolor{string}{HTML}{7B3294} +\definecolor{comment}{HTML}{1A9641} +\definecolor{identifier}{HTML}{000000} % Source code listings. -\usepackage{pxfonts} +\usepackage[scaled=0.80]{beramono} + \usepackage{listings} \lstset{ language={C++}, numbers=left, stepnumber=1, numbersep=6pt, - numberstyle=\footnotesize, + numberstyle=\small, breaklines=true, breakautoindent=true, breakatwhitespace=false, + numberblanklines=true, postbreak=\space, tabsize=2, - basicstyle=\ttfamily\small, + basicstyle=\ttfamily\normalsize, showspaces=false, showstringspaces=false, extendedchars=true, keywordstyle=\color{keyword}\bfseries, stringstyle=\color{string}\bfseries, - commentstyle=\color{comment}\bfseries, - identifierstyle=\color{identifier}, - frame=trbl + commentstyle=\color{comment}\bfseries\itshape, + identifierstyle=\color{identifier}, + frame=trbl, + aboveskip=3pt, + belowskip=3pt, + escapechar=@ + %moredelim=**[is][{\btHL[fill=green!30,draw=red,dashed,thin]}]{@}{@} } % Listings doesn't support UTF8. This is just enough for German umlauts. @@ -42,3 +59,50 @@ {ö}{{\"o}}1 {~}{{\textasciitilde}}1 } + +\makeatletter +\let\orig@lstnumber=\thelstnumber +\newcommand\lstresetnumber{\global\let\thelstnumber=\orig@lstnumber} +\let\orig@placelstnumber=\lst@PlaceNumber +\gdef\lst@PlaceNumber{\orig@placelstnumber\lstresetnumber} +\newcommand\lstsettmpnumber[1]{\gdef\thelstnumber{#1}} + +\lst@AddToHook{OnEmptyLine}{% + \ifnum\value{lstnumber}>99 + \lstsettmpnumber{\_\_\_} + \else\ifnum\value{lstnumber}>9 + \lstsettmpnumber{\_\_} + \else + \lstsettmpnumber{\_} + \fi\fi +% \lstsettmpnumber{\_\_\kern-6pt}% + \vspace{-1.75ex}% + \addtocounter{lstnumber}{-1}% +} +% old: (change numberblanklines=false!) +%\lst@AddToHook{OnEmptyLine}{% +% \vspace{\dimexpr\baselineskip+0.5em}% +% \addtocounter{lstnumber}{-1}% +%} + +\newenvironment{btHighlight}[1][] +{\begingroup\tikzset{bt@Highlight@par/.style={#1}}\begin{lrbox}{\@tempboxa}} +{\end{lrbox}\bt@HL@box[bt@Highlight@par]{\@tempboxa}\endgroup} + +\newcommand\btHL[1][]{% + \begin{btHighlight}[#1]\bgroup\aftergroup\bt@HL@endenv% + } + \def\bt@HL@endenv{% + \end{btHighlight}% + \egroup% +} +\newcommand{\bt@HL@box}[2][]{% + \tikz[#1]{% + \pgfpathrectangle{\pgfpoint{1pt}{0pt}}{\pgfpoint{\wd #2}{\ht #2}}% + \pgfusepath{use as bounding box}% + \node[anchor=base west, fill=orange!30,outer sep=0pt,inner xsep=2.2pt, inner ysep=0pt, rounded corners=3pt, minimum height=\ht\strutbox+1pt,#1]{\raisebox{1pt}{\strut}\strut\usebox{#2}}; + }% +} +\makeatother + +\newcommand{\hl}[1]{\btHL[fill=safeOrange,draw=black,thin]{#1}}
\ No newline at end of file diff --git a/latexHeaders/math.sty b/latexHeaders/math.sty new file mode 100644 index 0000000..c34cc99 --- /dev/null +++ b/latexHeaders/math.sty @@ -0,0 +1,98 @@ +% For Headlines with math +\usepackage{bm} + +% Display math. +\usepackage{amsmath} +\usepackage{mathtools} +\usepackage{amssymb} +\usepackage{ntheorem} + +%\usepackage{pxfonts} +\usepackage[scaled=0.945,largesc,looser]{newpxtext}%better than pxfonts... +\usepackage[scaled=0.945,bigdelims]{newpxmath} +\let\mathbb\vmathbb + +\DeclareFontFamily{LMX}{npxexx}{} +\DeclareFontShape{LMX}{npxexx}{m}{n}{<-> s * [1.045] zplexx}{} +\DeclareFontShape{LMX}{npxexx}{b}{n}{<-> s * [1.045] zplbexx}{} +%\DeclareFontShape{LMX}{npxexx}{m}{n}{<-> s * [0.78] zplexx}{} +%\DeclareFontShape{LMX}{npxexx}{b}{n}{<-> s * [0.78] zplbexx}{} +\DeclareFontShape{LMX}{npxexx}{bx}{n}{<->ssub * npxexx/b/n}{} + +%\usepackage[scaled=0.91]{XCharter} +%\usepackage[scaled=0.89,type1]{cabin}% sans serif +%\usepackage[charter,varbb,scaled=1.00,noxchvw]{newtxmath} + +%\usepackage{libertine} +%\usepackage[libertine]{newtxmath} + +% New enviroment for remarks. +\theoremstyle{break} +\newtheorem{bem}{Bemerkung} + +% New commands for math operators. +% Binomial coefficients. +\renewcommand{\binom}[2]{ + \Bigl( + \begin{matrix} + #1 \\ + #2 + \end{matrix} + \Bigr) +} +% Euler numbers, first kind. +\newcommand{\eulerI}[2]{ + \Bigl\langle + \begin{matrix} + #1 \\ + #2 + \end{matrix} + \Bigr\rangle +} +% Euler numbers, second kind. +\newcommand{\eulerII}[2]{ + \Bigl\langle\mkern-4mu\Bigl\langle + \begin{matrix} + #1 \\ + #2 + \end{matrix} + \Bigr\rangle\mkern-4mu\Bigr\rangle +} +% Stirling numbers, first kind. +\newcommand{\stirlingI}[2]{ + \Bigl[ + \begin{matrix} + #1 \\ + #2 + \end{matrix} + \Bigr] +} +% Stirling numbers, second kind. +\newcommand{\stirlingII}[2]{ + \Bigl\{ + \begin{matrix} + #1 \\ + #2 + \end{matrix} + \Bigr\} +} +% Legendre symbol. +\newcommand{\legendre}[2]{ + \Bigl( + \dfrac{#1}{#2} + \Bigr) +} +% Expectation values. +\newcommand{\E}{\text{E}} +% Greates common divisor. +\newcommand{\ggT}{\text{ggT}} +% sign for negative values +\newcommand{\sign}{\scalebox{0.66}[1.0]{\( - \)}} +% absolute values +\newcommand{\abs}[1]{\left|#1\right|} +% ceiling function +\newcommand{\ceil}[1]{\left\lceil#1\right\rceil} +% floor function +\newcommand{\floor}[1]{\left\lfloor#1\right\rfloor} +% multiplication +\renewcommand{\*}{\ensuremath{\cdotp}} diff --git a/latexHeaders/math.tex b/latexHeaders/math.tex deleted file mode 100644 index 9293898..0000000 --- a/latexHeaders/math.tex +++ /dev/null @@ -1,70 +0,0 @@ -% Display math. -\usepackage{amsmath} -\usepackage{mathtools} -\usepackage{amssymb} -\usepackage{ntheorem} - -% New enviroment for remarks. -\theoremstyle{break} -\newtheorem{bem}{Bemerkung} - -% New commands for math operators. -% Binomial coefficients. -\renewcommand{\binom}[2]{ - \biggl( - \begin{matrix} - #1 \\ - #2 - \end{matrix} - \biggr) -} -% Euler numbers, first kind. -\newcommand{\eulerI}[2]{ - \biggl\langle - \begin{matrix} - #1 \\ - #2 - \end{matrix} - \biggr\rangle -} -% Euler numbers, second kind. -\newcommand{\eulerII}[2]{ - \biggl\langle - \negthinspace - \biggl\langle - \begin{matrix} - #1 \\ - #2 - \end{matrix} - \biggr\rangle - \negthinspace - \biggr\rangle -} -% Stirling numbers, first kind. -\newcommand{\stirlingI}[2]{ - \biggl[ - \begin{matrix} - #1 \\ - #2 - \end{matrix} - \biggr] -} -% Stirling numbers, second kind. -\newcommand{\stirlingII}[2]{ - \biggl\{ - \begin{matrix} - #1 \\ - #2 - \end{matrix} - \biggr\} -} -% Legendre symbol. -\newcommand{\legendre}[2]{ - \biggl( - \frac{#1}{#2} - \biggr) -} -% Expectation values. -\newcommand{\E}{\text{E}} -% Greates common divisor. -\newcommand{\ggT}{\text{ggT}} diff --git a/math/berlekampMassey.cpp b/math/berlekampMassey.cpp new file mode 100644 index 0000000..4999254 --- /dev/null +++ b/math/berlekampMassey.cpp @@ -0,0 +1,30 @@ +vector<ll> BerlekampMassey(const vector<ll>& s) { + int n = sz(s), L = 0, m = 0; + vector<ll> C(n), B(n), T; + C[0] = B[0] = 1; + + ll b = 1; + for (int i = 0; i < n; i++) { + m++; + ll d = s[i] % mod; + for (int j = 1; j <= L; j++) { + d = (d + C[j] * s[i - j]) % mod; + } + if (!d) continue; + T = C; + ll coef = d * powMod(b, mod-2, mod) % mod; + for (int j = m; j < n; j++) { + C[j] = (C[j] - coef * B[j - m]) % mod; + } + if (2 * L > i) continue; + L = i + 1 - L; + B = T; + b = d; + m = 0; + } + + C.resize(L + 1); + C.erase(C.begin()); + for (auto& x : C) x = (mod - x) % mod; + return C; +}
\ No newline at end of file diff --git a/math/bigint.cpp b/math/bigint.cpp index e25ebe3..1753200 100644 --- a/math/bigint.cpp +++ b/math/bigint.cpp @@ -1,240 +1,276 @@ -// Bislang keine Division. Multiplikation nach Schulmethode. -#define PLUS 0 -#define MINUS 1 -#define BASE 1000000000 -#define EXPONET 9 - +// base and base_digits must be consistent +constexpr ll base = 1000000; +constexpr ll base_digits = 6; struct bigint { - int sign; - vector<ll> digits; - - // Initialisiert mit 0. - bigint(void) { sign = PLUS; } - - // Initialisiert mit kleinem Wert. - bigint(ll value) { - if (value == 0) sign = PLUS; - else { - sign = value >= 0 ? PLUS : MINUS; - value = abs(value); - while (value) { - digits.push_back(value % BASE); - value /= BASE; - }}} - - // Initialisiert mit C-String. Kann nicht mit Vorzeichen umgehen. - bigint(char *str, int length) { - int base = 1; - ll digit = 0; - for (int i = length - 1; i >= 0; i--) { - digit += base * (str[i] - '0'); - if (base * 10 == BASE) { - digits.push_back(digit); - digit = 0; - base = 1; - } else base *= 10; - } - if (digit != 0) digits.push_back(digit); - sign = PLUS; - } - - // Löscht führende Nullen und macht -0 zu 0. - void trim() { - while (digits.size() > 0 && digits[digits.size() - 1] == 0) - digits.pop_back(); - if (digits.size() == 0 && sign == MINUS) sign = PLUS; - } - - // Gibt die Zahl aus. - void print() { - if (digits.size() == 0) { printf("0"); return; } - if (sign == MINUS) printf("-"); - printf("%lld", digits[digits.size() - 1]); - for (int i = digits.size() - 2; i >= 0; i--) { - printf("%09lld", digits[i]); // Anpassen, wenn andere Basis gewählt wird. - }} -}; - -// Kleiner-oder-gleich-Vergleich. -bool operator<=(bigint &a, bigint &b) { - if (a.digits.size() == b.digits.size()) { - int idx = a.digits.size() - 1; - while (idx >= 0) { - if (a.digits[idx] < b.digits[idx]) return true; - else if (a.digits[idx] > b.digits[idx]) return false; - idx--; - } - return true; - } - return a.digits.size() < b.digits.size(); -} - -// Kleiner-Vergeleich. -bool operator<(bigint &a, bigint &b) { - if (a.digits.size() == b.digits.size()) { - int idx = a.digits.size() - 1; - while (idx >= 0) { - if (a.digits[idx] < b.digits[idx]) return true; - else if (a.digits[idx] > b.digits[idx]) return false; - idx--; - } - return false; - } - return a.digits.size() < b.digits.size(); -} - -void sub(bigint *a, bigint *b, bigint *c); - -// a + b = c. a, b, c dürfen gleich sein. -void add(bigint *a, bigint *b, bigint *c) { - if (a->sign == b->sign) c->sign = a->sign; - else { - if (a->sign == MINUS) { - a->sign ^= 1; - sub(b, a, c); - a->sign ^= 1; - } else { - b->sign ^= 1; - sub(a, b, c); - b->sign ^= 1; - } - return; - } - - c->digits.resize(max(a->digits.size(), b->digits.size())); - ll carry = 0; - int i = 0; - for (; i < (int)min(a->digits.size(), b->digits.size()); i++) { - ll sum = carry + a->digits[i] + b->digits[i]; - c->digits[i] = sum % BASE; - carry = sum / BASE; - } - if (i < (int)a->digits.size()) { - for (; i< (int)a->digits.size(); i++) { - ll sum = carry + a->digits[i]; - c->digits[i] = sum % BASE; - carry = sum / BASE; - } - } else { - for (; i< (int)b->digits.size(); i++) { - ll sum = carry + b->digits[i]; - c->digits[i] = sum % BASE; - carry = sum / BASE; - }} - if (carry) c->digits.push_back(carry); -} - -// a - b = c. c darf a oder b sein. a und b müssen verschieden sein. -void sub(bigint *a, bigint *b, bigint *c) { - if (a->sign == MINUS || b->sign == MINUS) { - b->sign ^= 1; - add(a, b, c); - b->sign ^= 1; - return; - } - - if (a < b) { - sub(b, a, c); - c->sign = MINUS; - c->trim(); - return; - } - - c->digits.resize(a->digits.size()); - ll borrow = 0; - int i = 0; - for (; i < (int)b->digits.size(); i++) { - ll diff = a->digits[i] - borrow - b->digits[i]; - if (a->digits[i] > 0) borrow = 0; - if (diff < 0) { - diff += BASE; - borrow = 1; - } - c->digits[i] = diff % BASE; - } - for (; i < (int)a->digits.size(); i++) { - ll diff = a->digits[i] - borrow; - if (a->digits[i] > 0) borrow = 0; - if (diff < 0) { - diff += BASE; - borrow = 1; - } - c->digits[i] = diff % BASE; - } - c->trim(); -} - -// Ziffernmultiplikation a * b = c. b und c dürfen gleich sein. -// a muss kleiner BASE sein. -void digitMul(ll a, bigint *b, bigint *c) { - if (a == 0) { - c->digits.clear(); - c->sign = PLUS; - return; - } - c->digits.resize(b->digits.size()); - ll carry = 0; - for (int i = 0; i < (int)b->digits.size(); i++) { - ll prod = carry + b->digits[i] * a; - c->digits[i] = prod % BASE; - carry = prod / BASE; - } - if (carry) c->digits.push_back(carry); - c->sign = (a > 0) ? b->sign : 1 ^ b->sign; - c->trim(); -} - -// Zifferndivision b / a = c. b und c dürfen gleich sein. -// a muss kleiner BASE sein. -void digitDiv(ll a, bigint *b, bigint *c) { - c->digits.resize(b->digits.size()); - ll carry = 0; - for (int i = (int)b->digits.size() - 1; i>= 0; i--) { - ll quot = (carry * BASE + b->digits[i]) / a; - carry = carry * BASE + b->digits[i] - quot * a; - c->digits[i] = quot; - } - c->sign = b->sign ^ (a < 0); - c->trim(); -} - -// a * b = c. c darf weder a noch b sein. a und b dürfen gleich sein. -void mult(bigint *a, bigint *b, bigint *c) { - bigint row = *a, tmp; - c->digits.clear(); - for (int i = 0; i < (int)b->digits.size(); i++) { - digitMul(b->digits[i], &row, &tmp); - add(&tmp, c, c); - row.digits.insert(row.digits.begin(), 0); - } - c->sign = a->sign != b->sign; - c->trim(); -} - -// Berechnet eine kleine Zehnerpotenz. -inline ll pow10(int n) { - ll res = 1; - for (int i = 0; i < n; i++) res *= 10; - return res; -} - -// Berechnet eine große Zehnerpotenz. -void power10(ll e, bigint *out) { - out->digits.assign(e / EXPONET + 1, 0); - if (e % EXPONET) - out->digits[out->digits.size() - 1] = pow10(e % EXPONET); - else out->digits[out->digits.size() - 1] = 1; -} - -// Nimmt eine Zahl module einer Zehnerpotenz 10^e. -void mod10(int e, bigint *a) { - int idx = e / EXPONET; - if ((int)a->digits.size() < idx + 1) return; - if (e % EXPONET) { - a->digits.resize(idx + 1); - a->digits[idx] %= pow10(e % EXPONET); - } else { - a->digits.resize(idx); - } - a->trim(); -} + vll a; ll sign; + + bigint() : sign(1) {} + + bigint(ll v) {*this = v;} + + bigint(const string &s) {read(s);} + + void operator=(const bigint& v) { + sign = v.sign; + a = v.a; + } + + void operator=(ll v) { + sign = 1; + if (v < 0) sign = -1, v = -v; + a.clear(); + for (; v > 0; v = v / base) + a.push_back(v % base); + } + + bigint operator+(const bigint& v) const { + if (sign == v.sign) { + bigint res = v; + for (ll i = 0, carry = 0; i < (ll)max(a.size(), v.a.size()) || carry; ++i) { + if (i == (ll)res.a.size()) + res.a.push_back(0); + res.a[i] += carry + (i < (ll)a.size() ? a[i] : 0); + carry = res.a[i] >= base; + if (carry) + res.a[i] -= base; + } + return res; + } + return *this - (-v); + } + + bigint operator-(const bigint& v) const { + if (sign == v.sign) { + if (abs() >= v.abs()) { + bigint res = *this; + for (ll i = 0, carry = 0; i < (ll)v.a.size() || carry; ++i) { + res.a[i] -= carry + (i < (ll)v.a.size() ? v.a[i] : 0); + carry = res.a[i] < 0; + if (carry) res.a[i] += base; + } + res.trim(); + return res; + } + return -(v - *this); + } + return *this + (-v); + } + + void operator*=(ll v) { + if (v < 0) sign = -sign, v = -v; + for (ll i = 0, carry = 0; i < (ll)a.size() || carry; ++i) { + if (i == (ll)a.size()) a.push_back(0); + ll cur = a[i] * v + carry; + carry = cur / base; + a[i] = cur % base; + } + trim(); + } + + bigint operator*(ll v) const { + bigint res = *this; + res *= v; + return res; + } + + friend pair<bigint, bigint> divmod(const bigint& a1, const bigint& b1) { + ll norm = base / (b1.a.back() + 1); + bigint a = a1.abs() * norm; + bigint b = b1.abs() * norm; + bigint q, r; + q.a.resize(a.a.size()); + for (ll i = (ll)a.a.size() - 1; i >= 0; i--) { + r *= base; + r += a.a[i]; + ll s1 = r.a.size() <= b.a.size() ? 0 : r.a[b.a.size()]; + ll s2 = r.a.size() <= b.a.size() - 1 ? 0 : r.a[b.a.size() - 1]; + ll d = (base * s1 + s2) / b.a.back(); + r -= b * d; + while (r < 0) + r += b, --d; + q.a[i] = d; + } + q.sign = a1.sign * b1.sign; + r.sign = a1.sign; + q.trim(); + r.trim(); + return make_pair(q, r / norm); + } + + bigint operator/(const bigint& v) const { + return divmod(*this, v).first; + } + + bigint operator%(const bigint& v) const { + return divmod(*this, v).second; + } + + void operator/=(ll v) { + if (v < 0) sign = -sign, v = -v; + for (ll i = (ll)a.size() - 1, rem = 0; i >= 0; --i) { + ll cur = a[i] + rem * base; + a[i] = cur / v; + rem = cur % v; + } + trim(); + } + + bigint operator/(ll v) const { + bigint res = *this; + res /= v; + return res; + } + + ll operator%(ll v) const { + if (v < 0) v = -v; + ll m = 0; + for (ll i = (ll)a.size() - 1; i >= 0; --i) + m = (a[i] + m * base) % v; + return m * sign; + } + + void operator+=(const bigint& v) { + *this = *this + v; + } + void operator-=(const bigint& v) { + *this = *this - v; + } + void operator*=(const bigint& v) { + *this = *this * v; + } + void operator/=(const bigint& v) { + *this = *this / v; + } + + bool operator<(const bigint& v) const { + if (sign != v.sign) return sign < v.sign; + if (a.size() != v.a.size()) + return a.size() * sign < v.a.size() * v.sign; + for (ll i = (ll)a.size() - 1; i >= 0; i--) + if (a[i] != v.a[i]) + return a[i] * sign < v.a[i] * sign; + return false; + } + + bool operator>(const bigint& v) const { + return v < *this; + } + bool operator<=(const bigint& v) const { + return !(v < *this); + } + bool operator>=(const bigint& v) const { + return !(*this < v); + } + bool operator==(const bigint& v) const { + return !(*this < v) && !(v < *this); + } + bool operator!=(const bigint& v) const { + return *this < v || v < *this; + } + + void trim() { + while (!a.empty() && !a.back()) a.pop_back(); + if (a.empty()) sign = 1; + } + + bool isZero() const { + return a.empty() || (a.size() == 1 && a[0] == 0); + } + + bigint operator-() const { + bigint res = *this; + res.sign = -sign; + return res; + } + + bigint abs() const { + bigint res = *this; + res.sign *= res.sign; + return res; + } + + ll longValue() const { + ll res = 0; + for (ll i = (ll)a.size() - 1; i >= 0; i--) + res = res * base + a[i]; + return res * sign; + } + + void read(const string& s) { + sign = 1; + a.clear(); + ll pos = 0; + while (pos < (ll)s.size() && (s[pos] == '-' || s[pos] == '+')) { + if (s[pos] == '-') sign = -sign; + ++pos; + } + for (ll i = (ll)s.size() - 1; i >= pos; i -= base_digits) { + ll x = 0; + for (ll j = max(pos, i - base_digits + 1); j <= i; j++) + x = x * 10 + s[j] - '0'; + a.push_back(x); + } + trim(); + } + + friend istream& operator>>(istream& stream, bigint& v) { + string s; + stream >> s; + v.read(s); + return stream; + } + + friend ostream& operator<<(ostream& stream, const bigint& v) { + if (v.sign == -1) stream << '-'; + stream << (v.a.empty() ? 0 : v.a.back()); + for (ll i = (ll)v.a.size() - 2; i >= 0; --i) + stream << setw(base_digits) << setfill('0') << v.a[i]; + return stream; + } + + static vll karatsubaMultiply(const vll& a, const vll& b) { + ll n = a.size(); + vll res(n + n); + if (n <= 32) { + for (ll i = 0; i < n; i++) + for (ll j = 0; j < n; j++) + res[i + j] += a[i] * b[j]; + return res; + } + ll k = n >> 1; + vll a1(a.begin(), a.begin() + k); + vll a2(a.begin() + k, a.end()); + vll b1(b.begin(), b.begin() + k); + vll b2(b.begin() + k, b.end()); + vll a1b1 = karatsubaMultiply(a1, b1); + vll a2b2 = karatsubaMultiply(a2, b2); + for (ll i = 0; i < k; i++) a2[i] += a1[i]; + for (ll i = 0; i < k; i++) b2[i] += b1[i]; + vll r = karatsubaMultiply(a2, b2); + for (ll i = 0; i < (ll)a1b1.size(); i++) r[i] -= a1b1[i]; + for (ll i = 0; i < (ll)a2b2.size(); i++) r[i] -= a2b2[i]; + for (ll i = 0; i < (ll)r.size(); i++) res[i + k] += r[i]; + for (ll i = 0; i < (ll)a1b1.size(); i++) res[i] += a1b1[i]; + for (ll i = 0; i < (ll)a2b2.size(); i++) res[i + n] += a2b2[i]; + return res; + } + + bigint operator*(const bigint& v) const { + vll a(this->a.begin(), this->a.end()); + vll b(v.a.begin(), v.a.end()); + while (a.size() < b.size()) a.push_back(0); + while (b.size() < a.size()) b.push_back(0); + while (a.size() & (a.size() - 1)) + a.push_back(0), b.push_back(0); + vll c = karatsubaMultiply(a, b); + bigint res; + res.sign = sign * v.sign; + for (ll i = 0, carry = 0; i < (ll)c.size(); i++) { + ll cur = c[i] + carry; + res.a.push_back(cur % base); + carry = cur / base; + } + res.trim(); + return res; + } +};
\ No newline at end of file diff --git a/math/binomial.cpp b/math/binomial.cpp index 9605820..fc6d980 100644 --- a/math/binomial.cpp +++ b/math/binomial.cpp @@ -1,10 +1,10 @@ -// Laufzeit: O(k) -ll calc_binom(ll n, ll k) { // Sehr sicher gegen Overflows. - ll r = 1, d; - if (k > n) return 0; - for (d = 1; d <= k; d++) { // Reihenfolge garantiert Teilbarkeit. - r *= n--; - r /= d; - } - return r; +ll calc_binom(ll n, ll k) { + ll r = 1, d; + if (k > n) return 0; + // Reihenfolge garantiert Teilbarkeit + for (d = 1; d <= k; d++) { + r *= n--; + r /= d; + } + return r; } diff --git a/math/binomial2.cpp b/math/binomial2.cpp new file mode 100644 index 0000000..2ddcfe9 --- /dev/null +++ b/math/binomial2.cpp @@ -0,0 +1,32 @@ +constexpr ll mod = 1000000009; + +ll binomPPow(ll n, ll k, ll p) { + ll res = 1; + if (p > n) { + } else if (p > n - k || (p * p > n && n % p < k % p)) { + res *= p; + res %= mod; + } else if (p * p <= n) { + ll c = 0, tmpN = n, tmpK = k; + while (tmpN > 0) { + if (tmpN % p < tmpK % p + c) { + res *= p; + res %= mod; + c = 1; + } else c = 0; + tmpN /= p; + tmpK /= p; + }} + return res; +} + +ll calc_binom(ll n, ll k) { + if (k > n) return 0; + ll res = 1; + k = min(k, n - k); + for (ll i = 0; primes[i] <= n; i++) { + res *= binomPPow(n, k, primes[i]); + res %= mod; + } + return res; +} diff --git a/math/binomial3.cpp b/math/binomial3.cpp new file mode 100644 index 0000000..760f2eb --- /dev/null +++ b/math/binomial3.cpp @@ -0,0 +1,9 @@ +ll calc_binom(ll n, ll k, ll p) { + if (n >= p || k > n) return 0; + ll x = k % 2 != 0 ? p-1 : 1; + for (ll c = p-1; c > n; c--) { + x *= c - k; x %= p; + x *= multInv(c, p); x %= p; + } + return x; +} diff --git a/math/chineseRemainder.cpp b/math/chineseRemainder.cpp index ac1b71e..2308836 100644 --- a/math/chineseRemainder.cpp +++ b/math/chineseRemainder.cpp @@ -1,15 +1,16 @@ // 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, falls alle teilerfremd sind). +// 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, falls alle teilerfremd sind). struct ChineseRemainder { - typedef __int128 lll; + using lll = __int128; vector<lll> lhs, rhs, mods, inv; lll M; // Produkt über die Moduli. Kann leicht überlaufen. ll g(vector<lll> &vec) { lll res = 0; - for (int i = 0; i < (int)vec.size(); i++) { + for (int i = 0; i < sz(vec); i++) { res += (vec[i] * inv[i]) % M; res %= M; } @@ -25,9 +26,10 @@ struct ChineseRemainder { // Löst das System. ll solve() { - M = accumulate(mods.begin(), mods.end(), lll(1), multiplies<lll>()); - inv.resize(lhs.size()); - for (int i = 0; i < (int)lhs.size(); i++) { + M = accumulate(mods.begin(), mods.end(), 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])); } diff --git a/math/cycleDetection.cpp b/math/cycleDetection.cpp new file mode 100644 index 0000000..621af82 --- /dev/null +++ b/math/cycleDetection.cpp @@ -0,0 +1,16 @@ +void cycleDetection(ll x0, function<ll(ll)> f) { + ll a = x0, b = f(x0), length = 1; + for (ll power = 1; a != b; b = f(b), length++) { + if (power == length) { + power *= 2; + length = 0; + a = b; + }} + ll start = 0; + a = x0; b = x0; + for (ll i = 0; i < length; i++) b = f(b); + while (a != b) { + a = f(a); + b = f(b); + start++; +}} diff --git a/math/discreteLogarithm.cpp b/math/discreteLogarithm.cpp index 6d3f656..d9227b9 100644 --- a/math/discreteLogarithm.cpp +++ b/math/discreteLogarithm.cpp @@ -1,13 +1,14 @@ -// Bestimmt Lösung x für a^x=b mod m. -ll solve (ll a, ll b, ll m) { // Laufzeit: O(sqrt(m)*log(m)) - ll n = (ll)sqrt((double)m) + 1; - map<ll,ll> vals; - for (int i = n; i >= 1; i--) vals[powMod(a, i * n, m)] = i; - for (int i = 0; i <= n; i++) { - ll cur = (powMod(a, i, m) * b) % m; - if (vals.count(cur)) { - ll ans = vals[cur] * n - i; - if (ans < m) return ans; - }} - return -1; +ll dlog(ll a, ll b, ll m) { + ll bound = sqrtl(m) + 1; //memory usage bound + map<ll, ll> vals; + for (ll i = 0, e = 1; i < bound; i++, e = (e * a) % m) { + vals[e] = i; + } + ll fact = powMod(a, m - bound - 1, m); + + for (ll i = 0; i < m; i += bound, b = (b * fact) % m) { + if (vals.count(b)) { + return i + vals[b]; + }} + return -1; } diff --git a/math/discreteNthRoot.cpp b/math/discreteNthRoot.cpp new file mode 100644 index 0000000..9249f73 --- /dev/null +++ b/math/discreteNthRoot.cpp @@ -0,0 +1,5 @@ +ll root(ll a, ll b, ll m) { + ll g = findPrimitive(m); + ll c = dlog(powMod(g, a, m), b, m); //diskreter logarithmus + return c < 0 ? -1 : powMod(g, c, m); +} diff --git a/math/divisors.cpp b/math/divisors.cpp new file mode 100644 index 0000000..638c87c --- /dev/null +++ b/math/divisors.cpp @@ -0,0 +1,11 @@ +ll countDivisors(ll n) { + ll res = 1; + for (ll i = 2; i * i * i <= n; i++) { + ll c = 0; + while (n % i == 0) {n /= i; c++;} + res *= c + 1; + } + if (isPrime(n)) res *= 2; + else if (n > 1) res *= isSquare(n) ? 3 : 4; + return res; +}
\ No newline at end of file diff --git a/math/extendedEuclid.cpp b/math/extendedEuclid.cpp index 66a6d93..d016a63 100644 --- a/math/extendedEuclid.cpp +++ b/math/extendedEuclid.cpp @@ -1,5 +1,6 @@ -ll extendedEuclid(ll a, ll b, ll &x, ll &y) { // a*x + b*y = ggt(a, b). - if (a == 0) { x = 0; y = 1; return b; } +// 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; diff --git a/math/fft.cpp b/math/fft.cpp deleted file mode 100644 index f09b7e3..0000000 --- a/math/fft.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Laufzeit: O(n log(n)). -typedef complex<double> cplx; // Eigene Implementierung ist schneller. - -// a.size() muss eine Zweierpotenz sein! -vector<cplx> fft(const vector<cplx> &a, bool inverse = 0) { - int logn = 1, n = a.size(); - vector<cplx> A(n); - while ((1 << logn) < n) logn++; - for (int i = 0; i < n; i++) { - int j = 0; - for (int k = 0; k < logn; k++) j = (j << 1) | ((i >> k) & 1); - A[j] = a[i]; - } - for (int s = 2; s <= n; s <<= 1) { - double angle = 2 * PI / s * (inverse ? -1 : 1); - cplx ws(cos(angle), sin(angle)); - for (int j = 0; j < n; j+= s) { - cplx w = 1; - for (int k = 0; k < s / 2; k++) { - cplx u = A[j + k], t = A[j + s / 2 + k]; - A[j + k] = u + w * t; - A[j + s / 2 + k] = u - w * t; - if (inverse) A[j + k] /= 2, A[j + s / 2 + k] /= 2; - w *= ws; - }}} - return A; -} - -// Polynome: a[0] = a_0, a[1] = a_1, ... und b[0] = b_0, b[1] = b_1, ... -// Bei Integern: Runde Koeffizienten: (int)round(a[i].real()) -vector<cplx> a = {0,0,0,0,1,2,3,4}, b = {0,0,0,0,2,3,0,1}; -a = fft(a); b = fft(b); -for (int i = 0; i < (int)a.size(); i++) a[i] *= b[i]; -a = fft(a,1); // a = a * b diff --git a/math/gauss.cpp b/math/gauss.cpp index b8e43d4..0c7ab03 100644 --- a/math/gauss.cpp +++ b/math/gauss.cpp @@ -1,8 +1,3 @@ -// Laufzeit: O(n^3) -void swapLines(int n, int l1, int l2) { - for (int i = 0; i <= n; i++) swap(mat[l1][i], mat[l2][i]); -} - void normalLine(int n, int line) { double factor = mat[line][line]; for (int i = 0; i <= n; i++) { @@ -17,7 +12,7 @@ void takeAll(int n, int line) { mat[i][j] -= diff * mat[line][j]; }}} -int gauss(int n) { // Gibt zurück, ob das System (eindeutig) lösbar ist. +int gauss(int n) { vector<bool> done(n, false); for (int i = 0; i < n; i++) { int swappee = i; // Sucht Pivotzeile für bessere Stabilität. @@ -25,12 +20,13 @@ int gauss(int n) { // Gibt zurück, ob das System (eindeutig) lösbar ist. if (done[j]) continue; if (abs(mat[j][i]) > abs(mat[i][i])) swappee = j; } - swapLines(n, i, swappee); + swap(mat[i], mat[swappee]); if (abs(mat[i][i]) > EPSILON) { normalLine(n, i); takeAll(n, i); done[i] = true; - }} // Ab jetzt nur noch checks bzgl. Eindeutigkeit/Existenz der Lösung. + }} + // Ab jetzt nur checks bzgl. Eindeutigkeit/Existenz der Lösung. for (int i = 0; i < n; i++) { bool allZero = true; for (int j = i; j < n; j++) diff --git a/math/gcd-lcm.cpp b/math/gcd-lcm.cpp index 2f09b7c..a1c63c8 100644 --- a/math/gcd-lcm.cpp +++ b/math/gcd-lcm.cpp @@ -1,3 +1,2 @@ -// Laufzeiten: O(log(a) + log(b)) -ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); } -ll lcm(ll a, ll b) { return a * (b / gcd(a, b)); } +ll gcd(ll a, ll b) {return b == 0 ? a : gcd(b, a % b);} +ll lcm(ll a, ll b) {return a * (b / gcd(a, b));} diff --git a/math/goldenSectionSearch.cpp b/math/goldenSectionSearch.cpp new file mode 100644 index 0000000..2be8c2d --- /dev/null +++ b/math/goldenSectionSearch.cpp @@ -0,0 +1,14 @@ +ld gss(ld l, ld r, function<ld(ld)> f) { + ld r = (sqrtl(5.0l)-1)/2; + ld x1 = b - r*(b-a), x2 = a + r*(b-a); + ld f1 = f(x1), f2 = f(x2); + for (int i = 0; i < 200; i++) { + if (f1 < f2) { //change to > to find maximum + b = x2; x2 = x1; f2 = f1; + x1 = b - r*(b-a); f1 = f(x1); + } else { + a = x1; x1 = x2; f1 = f2; + x2 = a + r*(b-a); f2 = f(x2); + }} + return a; +} diff --git a/math/inversions.cpp b/math/inversions.cpp index 0720407..02256ca 100644 --- a/math/inversions.cpp +++ b/math/inversions.cpp @@ -1,27 +1,9 @@ -// Laufzeit: O(n*log(n)) -ll merge(vector<ll> &v, vector<ll> &left, vector<ll> &right) { - int a = 0, b = 0, i = 0; - ll inv = 0; - while (a < (int)left.size() && b < (int)right.size()) { - if (left[a] < right[b]) v[i++] = left[a++]; - else { - inv += left.size() - a; - v[i++] = right[b++]; - } - } - while (a < (int)left.size()) v[i++] = left[a++]; - while (b < (int)right.size()) v[i++] = right[b++]; - return inv; -} - -ll mergeSort(vector<ll> &v) { // Sortiert v und gibt Inversionszahl zurück. - int n = v.size(); - vector<ll> left(n / 2), right((n + 1) / 2); - for (int i = 0; i < n / 2; i++) left[i] = v[i]; - for (int i = n / 2; i < n; i++) right[i - n / 2] = v[i]; - - ll result = 0; - if (left.size() > 1) result += mergeSort(left); - if (right.size() > 1) result += mergeSort(right); - return result + merge(v, left, right); +ll inversions(const vector<ll>& v) { + Tree<pair<ll, ll>> t; //ordered statistics tree + ll res = 0; + for (ll i = 0; i < (ll)v.size(); i++) { + res += i - t.order_of_key({v[i], i}); + t.insert({v[i], i}); + } + return res; } diff --git a/math/inversionsMerge.cpp b/math/inversionsMerge.cpp new file mode 100644 index 0000000..1ea2ada --- /dev/null +++ b/math/inversionsMerge.cpp @@ -0,0 +1,27 @@ +// Laufzeit: O(n*log(n)) +ll merge(vector<ll>& v, vector<ll>& left, vector<ll>& right) { + int a = 0, b = 0, i = 0; + ll inv = 0; + while (a < (int)left.size() && b < (int)right.size()) { + if (left[a] < right[b]) v[i++] = left[a++]; + else { + inv += left.size() - a; + v[i++] = right[b++]; + } + } + while (a < (int)left.size()) v[i++] = left[a++]; + while (b < (int)right.size()) v[i++] = right[b++]; + return inv; +} + +ll mergeSort(vector<ll> &v) { // Sortiert v und gibt Inversionszahl zurück. + int n = v.size(); + vector<ll> left(n / 2), right((n + 1) / 2); + for (int i = 0; i < n / 2; i++) left[i] = v[i]; + for (int i = n / 2; i < n; i++) right[i - n / 2] = v[i]; + + ll result = 0; + if (left.size() > 1) result += mergeSort(left); + if (right.size() > 1) result += mergeSort(right); + return result + merge(v, left, right); +} diff --git a/math/kthperm.cpp b/math/kthperm.cpp new file mode 100644 index 0000000..899dff1 --- /dev/null +++ b/math/kthperm.cpp @@ -0,0 +1,14 @@ +vector<ll> kthperm(ll k, ll n) { + Tree<ll> t; + vector<ll> res(n); + for (ll i = 1; i <= n; k /= i, i++) { + t.insert(i - 1); + res[n - i] = k % i; + } + for (ll& x : res) { + auto it = t.find_by_order(x); + x = *it; + t.erase(it); + } + return res; +} diff --git a/math/legendre.cpp b/math/legendre.cpp index adfd3c6..f08755f 100644 --- a/math/legendre.cpp +++ b/math/legendre.cpp @@ -1,17 +1,4 @@ -int legendre(ll a, ll p) { - a %= p; - if (a == 0) return 0; - if (a == 1 || p == 2) return 1; - if (a == 2) return (((p * p - 1) / 8) & 1) ? -1 : 1; - if (isPrime(a)) { - return legendre(p, a) * ((((p - 1) * (a - 1) / 4) & 1) ? -1 : 1); - } else { - map<ll, int> facts; - factor(a, facts); - int res = 1; - for (auto f : facts) - if (f.second & 1) - res *= legendre(f.first, p); - return res; - } +ll legendre(ll a, ll p) { + ll s = powMod(a, p / 2, p); + return s < 2 ? s : -1ll; } diff --git a/math/lgsFp.cpp b/math/lgsFp.cpp index 14549b7..52442c6 100644 --- a/math/lgsFp.cpp +++ b/math/lgsFp.cpp @@ -1,10 +1,5 @@ -// Laufzeit: O(n^3) -void swapLines(int n, int l1, int l2) { - for (int i = 0; i <= n; i++) swap(mat[l1][i], mat[l2][i]); -} - void normalLine(int n, int line, ll p) { - ll factor = multInv(mat[line][line], p); // Implementierung von oben. + ll factor = multInv(mat[line][line], p); for (int i = 0; i <= n; i++) { mat[line][i] *= factor; mat[line][i] %= p; @@ -23,8 +18,10 @@ void takeAll(int n, int line, ll p) { void gauss(int n, ll p) { // nx(n+1)-Matrix, Körper F_p. for (int line = 0; line < n; line++) { int swappee = line; - while (mat[swappee][line] == 0) swappee++; - swapLines(n, line, swappee); + while (swappee < n && mat[swappee][line] == 0) swappee++; + if (swappee == n) continue; + swap(mat[line], mat[swappee]); normalLine(n, line, p); takeAll(n, line, p); + // für Eindeutigkeit, Existenz etc. siehe LGS }} diff --git a/math/linearCongruence.cpp b/math/linearCongruence.cpp new file mode 100644 index 0000000..b4f172d --- /dev/null +++ b/math/linearCongruence.cpp @@ -0,0 +1,5 @@ +ll solveLinearCongruence(ll a, ll b, ll m) { + ll g = gcd(a, m); + if (b % g != 0) return -1; + return ((b / g) * multInv(a / g, m / g)) % (m / g); +}
\ No newline at end of file diff --git a/math/longestIncreasingSubsequence.cpp b/math/longestIncreasingSubsequence.cpp index 388e394..357ebcd 100644 --- a/math/longestIncreasingSubsequence.cpp +++ b/math/longestIncreasingSubsequence.cpp @@ -1,23 +1,23 @@ -vector<int> longestIncreasingSubsequence(vector<int> &seq) { - int n = seq.size(), lisLength = 0, lisEnd = 0; - vector<int> L(n), L_id(n), parents(n); - for (int i = 0; i < n; i++) { - int pos = - lower_bound(L.begin(), L.begin() + lisLength, seq[i]) - L.begin(); - L[pos] = seq[i]; - L_id[pos] = i; - parents[i] = pos ? L_id[pos - 1] : -1; - if (pos + 1 > lisLength) { - lisLength = pos + 1; - lisEnd = i; - }} - // Ab hier Rekonstruktion der Sequenz. - vector<int> result(lisLength); - int pos = lisLength - 1, x = lisEnd; - while (parents[x] >= 0) { - result[pos--] = x; - x = parents[x]; - } - result[0] = x; - return result; // Liste mit Indizes einer LIS. +vector<int> lis(vector<int> &seq) { + int n = seq.size(), lisLength = 0, lisEnd = 0; + vector<int> L(n), L_id(n), parents(n); + for (int i = 0; i < n; i++) { + int pos = upper_bound(L.begin(), L.begin() + lisLength, + seq[i]) - L.begin(); + L[pos] = seq[i]; + L_id[pos] = i; + parents[i] = pos ? L_id[pos - 1] : -1; + if (pos + 1 > lisLength) { + lisLength = pos + 1; + lisEnd = i; + }} + // Ab hier Rekonstruktion der Sequenz. + vector<int> result(lisLength); + int pos = lisLength - 1, x = lisEnd; + while (parents[x] >= 0) { + result[pos--] = x; + x = parents[x]; + } + result[0] = x; + return result; // Liste mit Indizes einer LIS. } diff --git a/math/math.tex b/math/math.tex index 1f372e5..4545927 100644 --- a/math/math.tex +++ b/math/math.tex @@ -1,58 +1,187 @@ \section{Mathe} -\subsection{ggT, kgV, erweiterter euklidischer Algorithmus} -\lstinputlisting{math/gcd-lcm.cpp} -\lstinputlisting{math/extendedEuclid.cpp} +\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} + \method{kthperm}{findet $k$-te Permutation \big($k \in [0, n!$)\big)}{n\*\log(n)} + \end{methods} + \sourcecode{math/kthperm.cpp} + \begin{methods} + \method{permIndex}{bestimmt index der Permutation \big($\mathit{res} \in [0, n!$)\big)}{n\*\log(n)} + \end{methods} + \sourcecode{math/permIndex.cpp} +\end{algorithm} \paragraph{Lemma von \textsc{Bézout}} -Sei $(x, y)$ eine Lösung für $ax + by = d$. +Sei $(x, y)$ eine Lösung der diophantischen Gleichung $ax + by = d$. Dann lassen sich wie folgt alle Lösungen berechnen: \[ - \left(x + k\frac{b}{\ggT(a, b)},~y - k\frac{a}{\ggT(a, b)}\right) +\left(x + k\frac{b}{\ggT(a, b)},~y - k\frac{a}{\ggT(a, b)}\right) \] -\paragraph{Multiplikatives Inverses von $x$ in $\mathbb{Z}/n\mathbb{Z}$} -Sei $0 \leq x < n$. Definiere $d := \ggT(x, n)$.\newline -\textbf{Falls $d = 1$:} -\begin{itemize}[nosep] - \item Erweiterter euklidischer Algorithmus liefert $\alpha$ und $\beta$ mit - $\alpha x + \beta n = 1$. - \item Nach Kongruenz gilt $\alpha x + \beta n \equiv \alpha x \equiv 1 \mod n$. - \item $x^{-1} :\equiv \alpha \mod n$ - \end{itemize} -\textbf{Falls $d \neq 1$:} Es existiert kein $x^{-1}$. -\lstinputlisting{math/multInv.cpp} +\paragraph{\textsc{Pell}-Gleichungen} +Sei $(\overline{x}, \overline{y})$ die Lösung von $x^2 - ny^2 = 1$, die $x>1$ minimiert. +Sei $(\tilde{x}, \tilde{y})$ die Lösung von $x^2-ny^2 = c$, die $x>1$ minimiert. Dann lassen +sich alle Lösungen von $x^2-ny^2=c$ berechnen durch: +\begin{align*} + x_1&\coloneqq \tilde{x}, & y_1&\coloneqq\tilde{y}\\ + x_{k+1}&\coloneqq \overline{x}x_k+n\overline{y}y_k, & y_{k+1}&\coloneqq\overline{x}y_k+\overline{y}x_k +\end{align*} -\subsection{Mod-Exponent über $\mathbb{F}_p$} -\lstinputlisting{math/modExp.cpp} -Iterativ: -\lstinputlisting{math/modPowIterativ.cpp} +\begin{algorithm}{ggT, kgV, erweiterter euklidischer Algorithmus} + \runtime{\log(a) + \log(b)} + \sourcecode{math/gcd-lcm.cpp} + \sourcecode{math/extendedEuclid.cpp} +\end{algorithm} -\subsection{Chinesischer Restsatz} +\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$ + +\textbf{Falls $\boldsymbol{\ggT(n, p) = 1}$:} \begin{itemize} - \item Extrem anfällig gegen Overflows. Evtl. häufig 128-Bit Integer verwenden. - \item Direkte Formel für zwei Kongruenzen $x \equiv a \mod n$, $x \equiv b \mod m$: - \[ - x \equiv a - y * n * \frac{a - b}{d} \mod \frac{mn}{d} - \qquad \text{mit} \qquad - d := \ggT(n, m) = yn + zm - \] - Formel kann auch für nicht teilerfremde Moduli verwendet werden. - Sind die Moduli nicht teilerfremd, existiert genau dann eine Lösung, - wenn $a\equiv~b \mod \ggT(m, n)$. - In diesem Fall sind keine Faktoren - auf der linken Seite erlaubt. + \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$ + \end{itemize} +\textbf{Sonst $\boldsymbol{\ggT(n, p) > 1}$:}\quad Es existiert kein $x^{-1}$. +\sourcecode{math/multInv.cpp} + +\begin{algorithm}{Lineare Kongruenz} + \begin{itemize} + \item Löst $ax\equiv b\pmod{m}$. + \item Weitere Lösungen unterscheiden sich um \raisebox{2pt}{$\frac{m}{g}$}, es gibt + also $g$ Lösungen modulo $m$. + \end{itemize} + \sourcecode{math/linearCongruence.cpp} +\end{algorithm} + +\subsection{Mod-Exponent und Multiplikation über $\boldsymbol{\mathbb{F}_p}$} +%\vspace{-1.25em} +%\begin{multicols}{2} + \method{mulMod}{berechnet $a \cdot b \bmod n$}{\log(b)} + \sourcecode{math/modMulIterativ.cpp} +% \vfill\null\columnbreak + \method{powMod}{berechnet $a^b \bmod n$}{\log(b)} + \sourcecode{math/modPowIterativ.cpp} +%\end{multicols} +%\vspace{-2.75em} +\begin{itemize} + \item für $a > 10^9$ \code{__int128} oder \code{modMul} benutzten! \end{itemize} -\lstinputlisting{math/chineseRemainder.cpp} - -\subsection{Primzahltest \& Faktorisierung} -\lstinputlisting{math/primes.cpp} -\subsection{Primzahlsieb von \textsc{Eratosthenes}} -\lstinputlisting{math/primeSieve.cpp} +\begin{algorithm}{Matrix-Exponent} + \begin{methods} + \method{precalc}{berechnet $m^{2^b}$ vor}{\log(b)\*n^3} + \method{calc}{berechnet $m^b_{y,x}$}{\log(b)\cdot n^2} + \end{methods} + \sourcecode{math/matrixPower.cpp} +\end{algorithm} + +\begin{algorithm}{Berlekamp-Massey} + \begin{methods} + \method{BerlekampMassey}{Berechnet ein lineare Recurenz $n$-ter Ordnung}{n^2} + \method{}{aus den ersten $2n$ Werte}{} + \end{methods} + \sourcecode{math/berlekampMassey.cpp} +\end{algorithm} + +\begin{algorithm}{Lineare-Recurenz} + Sei $f(n)=c_{n-1}f(n-1)+c_{n-2}f(n-2)+\dots + c_0f(0)$ eine Lineare Recurenz. Dann kann das \mbox{$k$-te} folgenglid in \runtime{n^3\log(k)} brechnet werden. + $$\renewcommand\arraystretch{1.5} + \setlength\arraycolsep{3pt} + \begin{pmatrix} + c_{n-1} & c_{n-2} & \smash{\cdots} & \smash{\cdots} & c_0 \\ + 1 & 0 & \smash{\cdots} & \smash{\cdots} & 0 \\ + 0 & \smash{\ddots} & \smash{\ddots} & & \smash{\vdots} \\ + 0 & \smash{\ddots} & \smash{\ddots} & \smash{\ddots} & \smash{\vdots} \\ + 0 & \smash{\cdots} & 0 & 1 & 0 \\ + \end{pmatrix}^k + \times~~ + \begin{pmatrix} + f(n-1) \\ + f(n-2) \\ + \smash{\vdots} \\ + \smash{\vdots} \\ + f(0) \\ + \end{pmatrix} + ~~=~~ + \begin{pmatrix} + f(n-1+k) \\ + f(n-2+k) \\ + \smash{\vdots} \\ + \smash{\vdots} \\ + f(k) \makebox[0pt][l]{\hspace{15pt}$\vcenter{\hbox{\huge$\leftarrow$}}$}\\ + \end{pmatrix} + $$ +\end{algorithm} + +\begin{algorithm}{Chinesischer Restsatz} + \begin{itemize} + \item Extrem anfällig gegen Overflows. Evtl. häufig 128-Bit Integer verwenden. + \item Direkte Formel für zwei Kongruenzen $x \equiv a \bmod n$, $x \equiv b \bmod m$: + \[ + x \equiv a - y \cdot n \cdot \frac{a - b}{d} \bmod \frac{mn}{d} + \qquad \text{mit} \qquad + d := \ggT(n, m) = yn + zm + \] + Formel kann auch für nicht teilerfremde Moduli verwendet werden. + Sind die Moduli nicht teilerfremd, existiert genau dann eine Lösung, + wenn $a\equiv~b \bmod \ggT(m, n)$. + In diesem Fall sind keine Faktoren + auf der linken Seite erlaubt. + \end{itemize} + \sourcecode{math/chineseRemainder.cpp} +\end{algorithm} -\subsection{\textsc{Euler}sche $\varphi$-Funktion} -\begin{itemize}[nosep] +\begin{algorithm}{Primzahltest \& Faktorisierung} + \begin{itemize} + \item für $n > 10^9$ \texttt{BigInteger} benutzten order \texttt{modMul}! + \end{itemize} + \method{isPrime}{prüft ob Zahl prim ist}{\log(n)^2} + \sourcecode{math/millerRabin.cpp} + \method{rho}{findet zufälligen Teiler}{\sqrt[\leftroot{3}\uproot{2}3]{n}} + \sourcecode{math/rho.cpp} + \method{squfof}{findet zufälligen Teiler}{\sqrt[\leftroot{4}\uproot{2}4]{n}} + \sourcecode{math/squfof.cpp} +\end{algorithm} + +\begin{algorithm}{Primzahlsieb von \textsc{Eratosthenes}} + \begin{itemize} + \item Kann erweitert werden: Für jede Zahl den \{kleinsten, größten\} Primfaktor speichern, Liste von Primzahlen berechnen + \item Bis $10^8$ in unter 64MB Speicher (lange Berechnung) + \end{itemize} + \begin{methods} + \method{primeSieve}{berechnet Primzahlen und Anzahl}{n\*\log(\log(n))} + \method{isPrime}{prüft ob Zahl prim ist}{1} + \end{methods} + \sourcecode{math/primeSieve.cpp} +\end{algorithm} + +\begin{algorithm}{Teiler} + \begin{methods} + \method{countDivisors}{Zählt teiler von $n$}{n^\frac{1}{3}} + \method{isPrime}{prüft ob Zahl prim ist}{1} + \end{methods} + \sourcecode{math/divisors.cpp} +\end{algorithm} + +\subsection{Primzahlzählfunktion $\boldsymbol{\pi}$} +\begin{methods} + \method{init}{berechnet $\pi$ bis $N$}{N\*\log(\log(N))} + \method{phi}{zählt zu $p_i$ teilerfremde Zahlen $\leq n$ für alle $i \leq k$}{???} + \method{pi}{zählt Primzahlen $\leq n$ ($n < N^2$)}{n^{2/3}} +\end{methods} +\sourcecode{math/piLehmer.cpp} + +\subsection{\textsc{Euler}sche $\boldsymbol{\varphi}$-Funktion} +\begin{itemize} \item Zählt die relativ primen Zahlen $\leq n$. \item Multiplikativ: @@ -61,67 +190,136 @@ Iterativ: \item $p$ prim, $k \in \mathbb{N}$: $~\varphi(p^k) = p^k - p^{k - 1}$ - \item $n = p_1^{a_1} \cdot \ldots \cdot p_k^{a_k}$: - $~\varphi(n) = n \cdot \left(1 - \frac{1}{p_1}\right) \cdot \ldots \cdot \left(1 - \frac{1}{p_k}\right)$ - Evtl. ist es sinnvoll obgien Code zum Faktorisieren zu benutzen und dann diese Formel anzuwenden. - \item \textbf{\textsc{Euler}'s Theorem:} - Seien $a$ und $m$ teilerfremd. Dann: - $a^{\varphi(m)} \equiv 1 \mod m$\newline + 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 \mod m$ + $a^{m} \equiv a \pmod{m}$ \end{itemize} -\lstinputlisting{math/phi.cpp} - -\subsection{Primitivwurzeln} -\begin{itemize}[nosep] - \item Primitivwurzel modulo $n$ existiert genau dann wenn: - \begin{itemize}[nosep] - \item $n$ ist $1$, $2$ oder $4$, oder - \item $n$ ist Potenz einer ungeraden Primzahl, oder - \item $n$ ist das Doppelte einer Potenz einer ungeraden Primzahl. +\sourcecode{math/phi.cpp} + +\begin{algorithm}{\textsc{Möbius}-Funktion und \textsc{Möbius}-Inversion} + \begin{itemize} + \item Seien $f,g : \mathbb{N} \to \mathbb{N}$ und $g(n) := \sum_{d \vert n}f(d)$. + Dann ist $f(n) = \sum_{d \vert n}g(d)\mu(\frac{n}{d})$. + \item $\sum\limits_{d \vert n}\mu(d) = + \begin{cases*} + 1 & falls $n = 1$\\ + 0 & sonst + \end{cases*}$ \end{itemize} - - \item Sei $g$ Primitivwurzel modulo $n$. - Dann gilt:\newline - Das kleinste $k$, sodass $g^k \equiv 1 \mod n$, ist $k = \varphi(n)$. -\end{itemize} -\lstinputlisting{math/primitiveRoot.cpp} - -\subsection{Diskreter Logarithmus} -\lstinputlisting{math/discreteLogarithm.cpp} - -\subsection{Binomialkoeffizienten} -\lstinputlisting{math/binomial.cpp} - -\subsection{LGS über $\mathbb{F}_p$} -\lstinputlisting{math/lgsFp.cpp} - -\subsection{LGS über $\mathbb{R}$} -\lstinputlisting{math/gauss.cpp} - -\subsection{Polynome \& FFT} + \textbf{Beispiel Inklusion/Exklusion:} + Gegeben sein eine Sequenz $A={a_1,\ldots,a_n}$ von Zahlen, $1 \leq a_i \leq N$. Zähle die Anzahl der \emph{coprime subsequences}.\newline + \textbf{Lösung}: + Für jedes $x$, sei $cnt[x]$ die Anzahl der Vielfachen von $x$ in $A$. + Es gibt $2^{cnt[x]}-1$ nicht leere Subsequences in $A$, die nur Vielfache von $x$ enthalten. + Die Anzahl der Subsequences mit $\ggT=1$ ist gegeben durch $\sum_{i = 1}^N \mu(i) \cdot (2^{cnt[i]} - 1)$. + \sourcecode{math/mobius.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}{Diskreter Logarithmus} + \begin{methods} + \method{solve}{bestimmt Lösung $x$ für $a^x=b \bmod m$}{\sqrt{m}\*\log(m)} + \end{methods} + \sourcecode{math/discreteLogarithm.cpp} +\end{algorithm} +%TODO +\begin{algorithm}{Diskrete \textrm{\textit{n}}-te Wurzel} + \begin{methods} + \method{root}{bestimmt Lösung $x$ für $x^a=b \bmod m$ }{\sqrt{m}\*\log(m)} + \end{methods} + Alle Lösungen haben die Form $g^{c + \frac{i \cdot \phi(n)}{\gcd(a, \phi(n))}}$ + \sourcecode{math/discreteNthRoot.cpp} +\end{algorithm} + +\subsection{LGS über $\boldsymbol{\mathbb{F}_p}$} +\method{gauss}{löst LGS}{n^3} +\sourcecode{math/lgsFp.cpp} + +\subsection{LGS über $\boldsymbol{\mathbb{R}}$} +\sourcecode{math/gauss.cpp} + +\begin{algorithm}{Polynome, FFT, NTT \& andere Transformationen} Multipliziert Polynome $A$ und $B$. -\begin{itemize}[nosep] - \item $\deg(A * B) = \deg(A) + \deg(B)$ - \item Vektoren \lstinline{a} und \lstinline{b} müssen mindestens Größe - $\deg(A * B) + 1$ haben. - Größe muss eine Zweierpotenz sein. - \item Für ganzzahlige Koeffizienten: \lstinline{(int)round(real(a[i]))} -\end{itemize} -\lstinputlisting{math/fft.cpp} - -\subsection{Numerisch Integrieren, Simpsonregel} -\lstinputlisting{math/simpson.cpp} - -\subsection{3D-Kugeln} -\lstinputlisting{math/spheres.cpp} - -\subsection{Longest Increasing Subsequence} -\lstinputlisting{math/longestIncreasingSubsequence.cpp} - -\subsection{Inversionszahl und Mergesort} -\lstinputlisting{math/inversions.cpp} + \begin{itemize} + \item $\deg(A \cdot B) = \deg(A) + \deg(B)$ + \item Vektoren \lstinline{a} und \lstinline{b} müssen mindestens Größe + $\deg(A \cdot B) + 1$ haben. + Größe muss eine Zweierpotenz sein. + \item Für ganzzahlige Koeffizienten: \lstinline{(int)round(real(a[i]))} + \item xor, or und and Transform funktioniert auch mit \code{double} oder modulo einer Primzahl $p$ falls $p \geq 2^{\texttt{bits}}$ + \end{itemize} + %\lstinputlisting{math/fft.cpp} + %\lstinputlisting{math/ntt.cpp} + %\textcolor{safeOrange}{$\blacksquare$} NTT code, %\textcolor{safeGreen}{$\blacksquare$} FFT code + \sourcecode{math/transforms/all.cpp} + \columnbreak + Für sehr viele transforms kann die Vertauschung vorberechnet werden: + \sourcecode{math/transforms/fftPerm.cpp} + Multiplikation mit 2 transforms statt 3: (nur benutzten wenn nötig!) + \sourcecode{math/transforms/fftMul.cpp} +\end{algorithm} + +\begin{algorithm}{Numerisch Integrieren, Simpsonregel} + \sourcecode{math/simpson.cpp} +\end{algorithm} + +\begin{algorithm}{Numerisch Extremstelle bestimmen} + \sourcecode{math/goldenSectionSearch.cpp} +\end{algorithm} + +\begin{algorithm}{Longest Increasing Subsequence} + \begin{itemize} + \item \code{lower\_bound} $\Rightarrow$ streng monoton + \item \code{upper\_bound} $\Rightarrow$ monoton + \end{itemize} + \sourcecode{math/longestIncreasingSubsequence.cpp} +\end{algorithm} + +\begin{algorithm}{Inversionszahl} + \sourcecode{math/inversions.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} \subsection{Satz von \textsc{Sprague-Grundy}} Weise jedem Zustand $X$ wie folgt eine \textsc{Grundy}-Zahl $g\left(X\right)$ zu: @@ -131,98 +329,22 @@ Weise jedem Zustand $X$ wie folgt eine \textsc{Grundy}-Zahl $g\left(X\right)$ zu \left\{g\left(Y\right) \mid Y \text{ von } X \text{ aus direkt erreichbar}\right\} \right\} \] -$X$ ist genau dann gewonnen, wenn $g\left(X\right) > 0$ ist.\\\\ +$X$ ist genau dann gewonnen, wenn $g\left(X\right) > 0$ ist.\\ Wenn man $k$ Spiele in den Zuständen $X_1, \ldots, X_k$ hat, dann ist die \textsc{Grundy}-Zahl des Gesamtzustandes $g\left(X_1\right) \oplus \ldots \oplus g\left(X_k\right)$. -\subsection{\textsc{Legendre}-Symbol} -Sei $p \geq 3$ eine Primzahl, $a \in \mathbb{Z}$: -\begin{align*} - \legendre{a}{p} &= - \begin{cases*} - 0 & falls $p~\vert~a$ \\[-1ex] - 1 & falls $\exists x \in \mathbb{Z}\backslash p\mathbb{Z} : a \equiv x^2 \mod p$ \\[-1ex] - -1 & sonst - \end{cases*} \\ - \legendre{-1}{p} &= (-1)^{\frac{p - 1}{2}} = - \begin{cases*} - 1 & falls $p \equiv 1 \mod 4$ \\[-1ex] - -1 & falls $p \equiv 3 \mod 4$ - \end{cases*} \\ - \legendre{2}{p} &= (-1)^{\frac{p^2 - 1}{8}} = - \begin{cases*} - 1 & falls $p \equiv \pm 1 \mod 8$ \\[-1ex] - -1 & falls $p \equiv \pm 3 \mod 8$ - \end{cases*} \\ - \legendre{p}{q} \cdot \legendre{q}{p} &= - (-1)^{\frac{p - 1}{2} \cdot \frac{q - 1}{2}} -\end{align*} -\lstinputlisting{math/legendre.cpp} - -\subsection{\textsc{Möbius}-Funktion und \textsc{Möbius}-Inversion} -\begin{itemize} - \item Seien $f,g : \mathbb{N} \to \mathbb{N}$ und $g(n) := \sum_{d \vert n}f(d)$. - Dann ist $f(n) = \sum_{d \vert n}g(d)\mu(\frac{n}{d})$. - \item $\sum_{d \vert n}\mu(d) = - \begin{cases*} - 1 & falls $n = 1$\\ - 0 & sonst - \end{cases*}$ -\end{itemize} -\textbf{Beispiel Inklusion/Exklusion:} -Gegeben sein eine Sequenz $A={a_1,\ldots,a_n}$ von Zahlen, $1 \leq a_i \leq N$. Zähle die Anzahl der \emph{coprime subsequences}.\newline -\textbf{Lösung}: -Für jedes $x$, sei $cnt[x]$ die Anzahl der Vielfachen von $x$ in $A$. -Es gibt $2^{cnt[x]}-1$ nicht leere Subsequences in $A$, die nur Vielfache von $x$ enthalten. -Die Anzahl der Subsequences mit $\ggT=1$ ist gegeben durch $\sum_{i = 1}^N \mu(i) \cdot (2^{cnt[i]} - 1)$. -\lstinputlisting{math/mobius.cpp} - \subsection{Kombinatorik} -\begin{flushleft} - \begin{tabular}{ll} - \toprule - \multicolumn{2}{c}{Berühmte Zahlen} \\ - \midrule - \textsc{Fibonacci} & - $f(0) = 0 \quad - f(1) = 1 \quad - f(n+2) = f(n+1) + f(n)$ \\ - - \textsc{Catalan} & - $C_0 = 1 \qquad - C_n = \sum\limits_{k = 0}^{n - 1} C_kC_{n - 1 - k} = - \frac{1}{n + 1}\binom{2n}{n} = \frac{2(2n - 1)}{n+1} \cdot C_{n-1}$ \\ - - \textsc{Euler} I & - $\eulerI{n}{0} = \eulerI{n}{n-1} = 1 \qquad - \eulerI{n}{k} = (k+1) \eulerI{n-1}{k} + (n-k) \eulerI{n-1}{k-1} $ \\ - - \textsc{Euler} II & - $\eulerII{n}{0} = 1 \quad - \eulerII{n}{n} = 0 \quad - \eulerII{n}{k} = (k+1) \eulerII{n-1}{k} + (2n-k-1) \eulerII{n-1}{k-1}$ \\ - - \textsc{Stirling} I & - $\stirlingI{0}{0} = 1 \qquad - \stirlingI{n}{0} = \stirlingI{0}{n} = 0 \qquad - \stirlingI{n}{k} = \stirlingI{n-1}{k-1} + (n-1) \stirlingI{n-1}{k}$ \\ - - \textsc{Stirling} II & - $\stirlingII{n}{1} = \stirlingII{n}{n} = 1 \qquad - \stirlingII{n}{k} = k \stirlingII{n-1}{k} + \stirlingII{n-1}{k-1}$ \\ - - \textsc{Bell} & - $B_1 = 1 \qquad - B_n = \sum\limits_{k = 0}^{n - 1} B_k\binom{n-1}{k} - = \sum\limits_{k = 0}^{n}\stirlingII{n}{k}$\\ - - \textsc{Partitions} & - $f(0,0) = 1 \quad - f(n,k) = 0 \text{ für } k > n \text{ oder } n \leq 0 \text{ oder } k \leq 0$ \\ - & $f(n,k) = f(n-k,k) + f(n-1,k-1)$ \\ - \bottomrule - \end{tabular} -\end{flushleft} +\paragraph{Wilsons Theorem} +A number $n$ is prime if and only if +$(n-1)!\equiv -1\bmod{n}$.\\ +($n$ is prime if and only if $(m-1)!\cdot(n-m)!\equiv(-1)^m\bmod{n}$ for all $m$ in $\{1,\dots,n\}$) +\begin{align*} + (n-1)!\equiv\begin{cases} + -1\bmod{n},&\mathrm{falls}~n \in \mathbb{P}\\ + \hphantom{-}2\bmod{n},&\mathrm{falls}~n = 4\\ + \hphantom{-}0\bmod{n},&\mathrm{sonst} + \end{cases} +\end{align*} \paragraph{\textsc{Zeckendorfs} Theorem} Jede positive natürliche Zahl kann eindeutig als Summe einer oder mehrerer @@ -231,340 +353,124 @@ aufeinanderfolgenden \textsc{Fibonacci}-Zahlen in der Summe vorkommen.\\ \emph{Lösung:} Greedy, nimm immer die größte \textsc{Fibonacci}-Zahl, die noch hineinpasst. +\paragraph{\textsc{Lucas}-Theorem} +Ist $p$ prim, $m=\sum_{i=0}^km_ip^i$, $n=\sum_{i=0}^kn_ip^i$ ($p$-adische Darstellung), +so gilt +\vspace{-0.75\baselineskip} +\[ + \binom{m}{n} \equiv \prod_{i=0}^k\binom{m_i}{n_i} \bmod{p}. +\] + +\subsection{The Twelvefold Way \textnormal{(verteile $n$ Bälle auf $k$ Boxen)}} +\input{math/tables/twelvefold} + \paragraph{\textsc{Catalan}-Zahlen} -\begin{itemize}[nosep] - \item Die erste und dritte angegebene Formel sind relativ sicher gegen Overflows. - \item Die erste Formel kann auch zur Berechnung der \textsc{Catalan}-Zahlen - bezüglich eines Moduls genutzt werden. - \item Die \textsc{Catalan}-Zahlen geben an: $C_n =$ - \begin{itemize}[nosep] +\begin{itemize} + \item Die \textsc{Catalan}-Zahl $C_n$ gibt an: + \begin{itemize} \item Anzahl der Binärbäume mit $n$ nicht unterscheidbaren Knoten. \item Anzahl der validen Klammerausdrücke mit $n$ Klammerpaaren. \item Anzahl der korrekten Klammerungen von $n+1$ Faktoren. - \item Anzahl der Möglichkeiten ein konvexes Polygon mit $n + 2$ Ecken in - Dreiecke zu zerlegen. + \item Anzahl der Möglichkeiten ein konvexes Polygon mit $n + 2$ Ecken in Dreiecke zu zerlegen. \item Anzahl der monotonen Pfade (zwischen gegenüberliegenden Ecken) in einem $n \times n$-Gitter, die nicht die Diagonale kreuzen. \end{itemize} \end{itemize} +\[C_0 = 1\qquad C_n = \sum\limits_{k = 0}^{n - 1} C_kC_{n - 1 - k} = +\frac{1}{n + 1}\binom{2n}{n} = \frac{4n - 2}{n+1} \cdot C_{n-1}\] +\begin{itemize} + \item Formel $1$ erlaubt berechnung ohne Division in \runtime{n^2} + \item Formel $2$ und $3$ erlauben berechnung in \runtime{n} +\end{itemize} \paragraph{\textsc{Euler}-Zahlen 1. Ordnung} Die Anzahl der Permutationen von $\{1, \ldots, n\}$ mit genau $k$ Anstiegen. Für die $n$-te Zahl gibt es $n$ mögliche Positionen zum Einfügen. Dabei wird entweder ein Ansteig in zwei gesplitted oder ein Anstieg um $n$ ergänzt. +\[\eulerI{n}{0} = \eulerI{n}{n-1} = 1 \quad +\eulerI{n}{k} = (k+1) \eulerI{n-1}{k} + (n-k) \eulerI{n-1}{k-1}= +\sum_{i=0}^{k} (-1)^i\binom{n+1}{i}(k+1-i)^n\] +\begin{itemize} + \item Formel $1$ erlaubt berechnung ohne Division in \runtime{n^2} + \item Formel $2$ erlaubt berechnung in \runtime{n\log(n)} +\end{itemize} \paragraph{\textsc{Euler}-Zahlen 2. Ordnung} Die Anzahl der Permutationen von $\{1,1, \ldots, n,n\}$ mit genau $k$ Anstiegen. +\[\eulerII{n}{0} = 1 \qquad\eulerII{n}{n} = 0 \qquad\eulerII{n}{k} = (k+1) \eulerII{n-1}{k} + (2n-k-1) \eulerII{n-1}{k-1}\] +\begin{itemize} + \item Formel erlaubt berechnung ohne Division in \runtime{n^2} +\end{itemize} \paragraph{\textsc{Stirling}-Zahlen 1. Ordnung} Die Anzahl der Permutationen von $\{1, \ldots, n\}$ mit genau $k$ Zyklen. Es gibt zwei Möglichkeiten für die $n$-te Zahl. Entweder sie bildet einen eigene Zyklus, oder sie kann an jeder Position in jedem Zyklus einsortiert werden. +\[\stirlingI{0}{0} = 1 \qquad +\stirlingI{n}{0} = \stirlingI{0}{n} = 0 \qquad +\stirlingI{n}{k} = \stirlingI{n-1}{k-1} + (n-1) \stirlingI{n-1}{k}\] +\begin{itemize} + \item Formel erlaubt berechnung ohne Division in \runtime{n^2} +\end{itemize} \paragraph{\textsc{Stirling}-Zahlen 2. Ordnung} Die Anzahl der Möglichkeiten $n$ Elemente in $k$ nichtleere Teilmengen zu zerlegen. Es gibt $k$ Möglichkeiten die $n$ in eine $n-1$-Partition einzuordnen. Dazu kommt der Fall, dass die $n$ in ihrer eigenen Teilmenge (alleine) steht. +\[\stirlingII{n}{1} = \stirlingII{n}{n} = 1 \qquad +\stirlingII{n}{k} = k \stirlingII{n-1}{k} + \stirlingII{n-1}{k-1} = +\frac{1}{k!} \sum\limits_{i=0}^{k} (-1)^{k-i}\binom{k}{i}i^n\] +\begin{itemize} + \item Formel $1$ erlaubt berechnung ohne Division in \runtime{n^2} + \item Formel $2$ erlaubt berechnung in \runtime{n\log(n)} +\end{itemize} \paragraph{\textsc{Bell}-Zahlen} Anzahl der Partitionen von $\{1, \ldots, n\}$. Wie \textsc{Striling}-Zahlen 2. Ordnung ohne Limit durch $k$. +\[B_1 = 1 \qquad +B_n = \sum\limits_{k = 0}^{n - 1} B_k\binom{n-1}{k} += \sum\limits_{k = 0}^{n}\stirlingII{n}{k}\qquad\qquad B_{p^m+n}\equiv m\cdot B_n + B_{n+1} \bmod{p}\] + +\paragraph{Partitions} +Die Anzahl der Partitionen von $n$ in genau $k$ positive Summanden. +Die Anzahl der Partitionen von $n$ mit Elementen aus ${1,\dots,k}$. +\begin{align*} + p_0(0)=1 \qquad p_k(n)&=0 \text{ für } k > n \text{ oder } n \leq 0 \text{ oder } k \leq 0\\ + p_k(n)&= p_k(n-k) + p_{k-1}(n-1)\\[2pt] + p(n)&=\sum_{k=1}^{n} p_k(n)=p_n(2n)=\sum\limits_{k\neq0}^\infty(-1)^{k+1}p\bigg(n - \frac{k(3k-1)}{2}\bigg) +\end{align*} +\begin{itemize} + \item in Formel $3$ kann abgebrochen werden wenn $\frac{k(3k-1)}{2} > n$. + \item Die Anzahl der Partitionen von $n$ in bis zu $k$ positive Summanden ist $\sum\limits_{i=0}^{k}p_i(n)=p_k(n+k)$. +\end{itemize} -\paragraph{Integer Partitions} -Anzahl der Teilmengen von $\mathbb{N}$, die sich zu $n$ aufaddieren mit maximalem Elment $\leq k$.\\ - -\begin{tabular}{lcr} - \toprule - \multicolumn{3}{c}{Binomialkoeffizienten} \\ - \midrule - $\binom{n}{k} = \frac{n!}{k!(n - k)!}$ & - $\binom{n}{k} = \binom{n - 1}{k} + \binom{n - 1}{k - 1}$ & - $\sum\limits_{k = 0}^n\binom{r + k}{k} = \binom{r + n + 1}{n}$ \\ - - $\sum\limits_{k = 0}^n \binom{n}{k} = 2^n$ & - $\binom{n}{m}\binom{m}{k} = \binom{n}{k}\binom{n - k}{m - k}$ & - $\binom{n}{k} = (-1)^k \binom{k - n - 1}{k}$ \\ - - $\binom{n}{k} = \binom{n}{n - k}$ & - $\sum\limits_{k = 0}^n \binom{k}{m} = \binom{n + 1}{m + 1}$ & - $\sum\limits_{i = 0}^n \binom{n}{i}^2 = \binom{2n}{n}$ \\ - - $\binom{n}{k} = \frac{n}{k}\binom{n - 1}{k - 1}$ & - $\sum\limits_{k = 0}^n \binom{r}{k}\binom{s}{n - k} = \binom{r + s}{n}$ & - $\sum\limits_{i = 1}^n \binom{n}{i} F_i = F_{2n} \quad F_n = n\text{-th Fib.}$ \\ - \bottomrule -\end{tabular} -\vspace{1mm} - -\begin{tabular}{l|l|l} - \toprule - \multicolumn{3}{c}{Reihen} \\ - \midrule - $\sum\limits_{i = 1}^n i = \frac{n(n+1)}{2}$ & - $\sum\limits_{i = 1}^n i^2 = \frac{n(n + 1)(n + 2)}{6}$ & - $\sum\limits_{i = 1}^n i^3 = \frac{n^2 (n + 1)^2}{4}$ \\ - - $\sum\limits_{i = 0}^n c^i = \frac{c^{n + 1} - 1}{c - 1} \quad c \neq 1$ & - $\sum\limits_{i = 0}^\infty c^i = \frac{1}{1 - c} \quad \vert c \vert < 1$ & - $\sum\limits_{i = 1}^\infty c^i = \frac{c}{1 - c} \quad \vert c \vert < 1$ \\ - - \multicolumn{2}{l|}{ - $\sum\limits_{i = 0}^n ic^i = \frac{nc^{n + 2} - (n + 1)c^{n + 1} + c}{(c - 1)^2} \quad c \neq 1$ - } & - $\sum\limits_{i = 0}^\infty ic^i = \frac{c}{(1 - c)^2} \quad \vert c \vert < 1$ \\ - - $H_n = \sum\limits_{i = 1}^n \frac{1}{i}$ & - \multicolumn{2}{l}{ - $\sum\limits_{i = 1}^n iH_i = \frac{n(n + 1)}{2}H_n - \frac{n(n - 1)}{4}$ - } \\ - - $\sum\limits_{i = 1}^n H_i = (n + 1)H_n - n$ & - \multicolumn{2}{l}{ - $\sum\limits_{i = 1}^n \binom{i}{m}H_i = - \binom{n + 1}{m + 1} \left(H_{n + 1} - \frac{1}{m + 1}\right)$ - } \\ - \bottomrule -\end{tabular} -\vspace{1mm} - -\begin{tabular}{l|r} - \toprule - \multicolumn{2}{c}{ - Wahrscheinlichkeitstheorie ($A,B$ Ereignisse und $X,Y$ Variablen) - } \\ - \midrule - $\E(X + Y) = \E(X) + \E(Y)$ & - $\E(\alpha X) = \alpha \E(X)$ \\ - - $X, Y$ unabh. $\Leftrightarrow \E(XY) = \E(X) \cdot \E(Y)$ & - $\Pr[A \vert B] = \frac{\Pr[A \land B]}{\Pr[B]}$ \\ - - $\Pr[A \lor B] = \Pr[A] + \Pr[B] - \Pr[A \land B]$ & - $\Pr[A \land B] = \Pr[A] \cdot \Pr[B]$ \\ - \bottomrule -\end{tabular} -\vspace{1mm} - -\begin{tabular}{lr|lr} - \toprule - \multicolumn{4}{c}{\textsc{Bertrand}'s Ballot Theorem (Kandidaten $A$ und $B$, $k \in \mathbb{N}$)} \\ - \midrule - $\#A > k\#B$ & $Pr = \frac{a - kb}{a + b}$ & - $\#B - \#A \leq k$ & $Pr = 1 - \frac{a!b!}{(a + k + 1)!(b - k - 1)!}$ \\ - - $\#A \geq k\#B$ & $Pr = \frac{a + 1 - kb}{a + 1}$ & - $\#A \geq \#B + k$ & $Num = \frac{a - k + 1 - b}{a - k + 1} \binom{a + b - k}{b}$ \\ - \bottomrule -\end{tabular} -\vspace{5mm} - -\begin{tabular}{c|cccc} - \toprule - \multicolumn{5}{c}{The Twelvefold Way (verteile $n$ Bälle auf $k$ Boxen)} \\ - \midrule - Bälle & identisch & unterscheidbar & identisch & unterscheidbar \\ - Boxen & identisch & identisch & unterscheidbar & unterscheidbar \\ - \midrule - - & - $p_k(n)$ & - $\sum\limits_{i = 0}^k \stirlingII{n}{i}$ & - $\binom{n + k - 1}{k - 1}$ & - $k^n$ \\ - - size $\geq 1$ & - $p(n, k)$ & - $\stirlingII{n}{k}$ & - $\binom{n - 1}{k - 1}$ & - $k! \stirlingII{n}{k}$ \\ - - size $\leq 1$ & - $[n \leq k]$ & - $[n \leq k]$ & - $\binom{k}{n}$ & - $n! \binom{k}{n}$ \\ - \midrule - \multicolumn{5}{l}{ - $p_k(n)$: \#Anzahl der Partitionen von $n$ in $\leq k$ positive Summanden. - } \\ - \multicolumn{5}{l}{ - $p(n, k)$: \#Anzahl der Partitionen von $n$ in genau $k$ positive Summanden. - } \\ - \multicolumn{5}{l}{ - $[\text{Bedingung}]$: \lstinline{return Bedingung ? 1 : 0;} - } \\ - \bottomrule -\end{tabular} -\vspace{1mm} - -\begin{flushleft} - \begin{tabular}{l|cccl} - \toprule - \multicolumn{5}{c}{Platonische Körper} \\ - \midrule - Übersicht & Seiten & Ecken & Kanten & dual zu \\ - \midrule - Tetraeder & 4 & 4 & 6 & Tetraeder \\ - Würfel/Hexaeder & 6 & 8 & 12 & Oktaeder \\ - Oktaeder & 8 & 6 & 12 & Würfel/Hexaeder\\ - Dodekaeder & 12 & 20 & 30 & Ikosaeder \\ - Ikosaeder & 20 & 12 & 30 & Dodekaeder \\ - \midrule - \multicolumn{5}{c}{Färbungen mit maximal $n$ Farben (bis auf Isomorphie)} \\ - \midrule - \multicolumn{3}{l}{Ecken vom Oktaeder/Seiten vom Würfel} & - \multicolumn{2}{l}{$(n^6 + 3n^4 + 12n^3 + 8n^2)/24$} \\ - - \multicolumn{3}{l}{Ecken vom Würfel/Seiten vom Oktaeder} & - \multicolumn{2}{l}{$(n^8 + 17n^4 + 6n^2)/24$} \\ - - \multicolumn{3}{l}{Kanten vom Würfel/Oktaeder} & - \multicolumn{2}{l}{$(n^{12} + 6n^7 + 3n^6 + 8n^4 + 6n^3)/24$} \\ - - \multicolumn{3}{l}{Ecken/Seiten vom Tetraeder} & - \multicolumn{2}{l}{$(n^4 + 11n^2)/12$} \\ - - \multicolumn{3}{l}{Kanten vom Tetraeder} & - \multicolumn{2}{l}{$(n^6 + 3n^4 + 8n^2)/12$} \\ - - \multicolumn{3}{l}{Ecken vom Ikosaeder/Seiten vom Dodekaeder} & - \multicolumn{2}{l}{$(n^{12} + 15n^6 + 44n^4)/60$} \\ - - \multicolumn{3}{l}{Ecken vom Dodekaeder/Seiten vom Ikosaeder} & - \multicolumn{2}{l}{$(n^{20} + 15n^{10} + 20n^8 + 24n^4)/60$} \\ - - \multicolumn{3}{l}{Kanten vom Dodekaeder/Ikosaeder (evtl. falsch)} & - \multicolumn{2}{l}{$(n^{30} + 15n^{16} + 20n^{10} + 24n^6)/60$} \\ - \bottomrule - \end{tabular} -\end{flushleft} -\vspace{1mm} - -\begin{tabular}{p{4.3cm}|p{7cm}} - \toprule - \multicolumn{2}{c}{Nim-Spiele (\ding{182} letzter gewinnt (normal), \ding{183} letzter verliert)} \\ - \midrule - Beschreibung & - Strategie \\ - \midrule - - $M = [\mathit{pile}_i]$\newline - $[x] := \{1, \ldots, x\}$& - $\mathit{SG} = \oplus_{i = 1}^n \mathit{pile}_i$\newline - \ding{182} Nimm von einem Stapel, sodass $\mathit{SG}$ $0$ wird.\newline - \ding{183} Genauso. - Außer: Bleiben nur noch Stapel der Größe $1$, erzeuge ungerade Anzahl solcher Stapel.\\ - \midrule - - $M = \{a^m \mid m \geq 0\}$ & - $a$ ungerade: $\mathit{SG}_n = n \% 2$\newline - $a$ gerade:\newline - $\mathit{SG}_n = 2$, falls $n \equiv a \mod (a + 1) $\newline - $\mathit{SG}_n = n \% (a + 1) \% 2$, sonst.\\ - \midrule - - $M_{\text{\ding{172}}} = \left[\frac{\mathit{pile}_i}{2}\right]$\newline - $M_{\text{\ding{173}}} = - \left\{\left\lceil\frac{\mathit{pile}_i}{2}\right\rceil, - \mathit{pile}_i\right\}$ & - \ding{172} - $\mathit{SG}_{2n} = n$, - $\mathit{SG}_{2n+1} = \mathit{SG}_n$\newline - \ding{173} - $\mathit{SG}_0 = 0$, - $\mathit{SG}_n = [\log_2 n] + 1$ \\ - \midrule - - $M_{\text{\ding{172}}} = \text{Teiler von $\mathit{pile}_i$}$\newline - $M_{\text{\ding{173}}} = \text{echte Teiler von $\mathit{pile}_i$}$ & - \ding{172} - $\mathit{SG}_0 = 0$, - $\mathit{SG}_n = \mathit{SG}_{\text{\ding{173},n}} + 1$\newline - \ding{173} - $\mathit{ST}_1 = 0$, - $\mathit{SG}_n = \text{\#Nullen am Ende von $n_{bin}$}$\\ - \midrule - - $M_{\text{\ding{172}}} = [k]$\newline - $M_{\text{\ding{173}}} = S$, ($S$ endlich)\newline - $M_{\text{\ding{174}}} = S \cup \{\mathit{pile}_i\}$ & - $\mathit{SG}_{\text{\ding{172}}, n} = n \mod (k + 1)$\newline - \ding{182} Niederlage bei $\mathit{SG} = 0$\newline - \ding{183} Niederlage bei $\mathit{SG} = 1$\newline - $\mathit{SG}_{\text{\ding{174}}, n} = \mathit{SG}_{\text{\ding{173}}, n} + 1$\\ - \midrule - - \multicolumn{2}{l}{ - Für jedes endliche $M$ ist $\mathit{SG}$ eines Stapels irgendwann periodisch. - } \\ - \midrule - - \textsc{Moore}'s Nim:\newline - Beliebige Zahl von maximal $k$ Stapeln. & - \ding{182} - Schreibe $\mathit{pile}_i$ binär. - Addiere ohne Übertrag zur Basis $k + 1$. - Niederlage, falls Ergebnis gleich 0.\newline - \ding{183} - Wenn alle Stapel $1$ sind: - Niederlage, wenn $n \equiv 1 \mod (k + 1)$. - Sonst wie in \ding{182}.\\ - \midrule - - Staircase Nim:\newline - $n$ Stapel in einer Reihe. - Beliebige Zahl von Stapel $i$ nach Stapel $i-1$. & - Niederlage, wenn Nim der ungeraden Spiele verloren ist:\newline - $\oplus_{i = 0}^{(n - 1) / 2} \mathit{pile}_{2i + 1} = 0$\\ - \midrule - - \textsc{Lasker}'s Nim:\newline - Zwei mögliche Züge:\newline - 1) Nehme beliebige Zahl.\newline - 2) Teile Stapel in zwei Stapel (ohne Entnahme).& - $\mathit{SG}_n = n$, falls $n \equiv 1,2 \mod 4$\newline - $\mathit{SG}_n = n + 1$, falls $n \equiv 3 \mod 4$\newline - $\mathit{SG}_n = n - 1$, falls $n \equiv 0 \mod 4$\\ - \midrule - - \textsc{Kayles}' Nim:\newline - Zwei mögliche Züge:\newline - 1) Nehme beliebige Zahl.\newline - 2) Teile Stapel in zwei Stapel (mit Entnahme).& - Berechne $\mathit{SG}_n$ für kleine $n$ rekursiv.\newline - $n \in [72,83]: \quad 4, 1, 2, 8, 1, 4, 7, 2, 1, 8, 2, 7$\newline - Periode ab $n = 72$ der Länge $12$.\\ - \bottomrule -\end{tabular} - -\begin{tabular}{ll} - \toprule - \multicolumn{2}{c}{Verschiedenes} \\ - \midrule - Türme von Hanoi, minimale Schirttzahl: & - $T_n = 2^n - 1$ \\ - - \#Regionen zwischen $n$ Gearden & - $\frac{n\left(n + 1\right)}{2} + 1$ \\ - - \#geschlossene Regionen zwischen $n$ Geraden & - $\frac{n^2 - 3n + 2}{2}$ \\ - - \#markierte, gewurzelte Bäume & - $n^{n-1}$ \\ - - \#markierte, nicht gewurzelte Bäume & - $n^{n-2}$ \\ - - \#Wälder mit $k$ gewurzelten Bäumen & - $\frac{k}{n}\binom{n}{k}n^{n-k}$ \\ - - Derangements & - $!n = (n - 1)(!(n - 1) + !(n - 2))$ \\ - & - $!n = \left\lfloor\frac{n!}{e} + \frac{1}{2}\right\rfloor$ \\ - & - $\lim\limits_{n \to \infty} \frac{!n}{n!} = \frac{1}{e}$ \\ - \bottomrule -\end{tabular} - -% \subsection{Big Integers} -% \lstinputlisting{math/bigint.cpp} +\paragraph{Binomialkoeffizienten} +Die Anzahl der \mbox{$k$-elementigen} Teilmengen einer \mbox{$n$-elementigen} Menge. + +\textbf{WICHTIG:} Binomialkoeffizient in \runtime{1} berechnen indem man $x!$ vorberechnet. + +%\begin{algorithm}{Binomialkoeffizienten} + \begin{methods} + \method{calc\_binom}{berechnet Binomialkoeffizient $(n \le 61)$}{k} + \end{methods} + \sourcecode{math/binomial.cpp} + +\columnbreak + \begin{methods} + \method{calc\_binom}{berechnet Binomialkoeffizient modulo Primzahl $p$}{p-n} + \end{methods} + \textbf{Wichtig:} $p > n$ + \sourcecode{math/binomial3.cpp} + + \begin{methods} + \method{calc\_binom}{berechnet Primfaktoren vom Binomialkoeffizient}{n} + \end{methods} + \textbf{WICHTIG:} braucht alle Primzahlen $\leq n$ + \sourcecode{math/binomial2.cpp} +%\end{algorithm} + +%\input{math/tables/numbers} + +\begin{algorithm}[optional]{Big Integers} + \sourcecode{math/bigint.cpp} +\end{algorithm} diff --git a/math/matrixPower.cpp b/math/matrixPower.cpp new file mode 100644 index 0000000..d3c1b63 --- /dev/null +++ b/math/matrixPower.cpp @@ -0,0 +1,16 @@ +vector<mat> pows; + +void precalc(mat m) { + pows = {mat(1), m}; + for (int i = 1; i < 60; i++) pows.push_back(pows[i] * pows[i]); +} + +ll calc(int x, int y, ll b) { + vector<ll> v(pows[0].m.size()); + v[x] = 1; + for (ll i = 1; b > 0; i++) { + if (b & 1) v = pows[i] * v; + b /= 2; + } + return v[y]; +}
\ No newline at end of file diff --git a/math/millerRabin.cpp b/math/millerRabin.cpp new file mode 100644 index 0000000..fc9385a --- /dev/null +++ b/math/millerRabin.cpp @@ -0,0 +1,20 @@ +constexpr ll bases32[] = {2, 7, 61}; +constexpr ll bases64[] = {2, 325, 9375, 28178, 450775, + 9780504, 1795265022}; + +bool isPrime(ll n) { + if(n < 2 || n % 2 == 0) return n == 2; + ll d = n - 1, j = 0; + while(d % 2 == 0) d /= 2, j++; + for(ll a : bases64) { + if (a % n == 0) continue; + ll v = powMod(a, d, n); + if(v == 1 || v == n - 1) continue; + for(ll i = 1; i <= j; i++) { + v = (v * v) % n; //mulmod or int128 + if(v == n - 1 || v <= 1) break; + } + if(v != n - 1) return false; + } + return true; +} diff --git a/math/mobius.cpp b/math/mobius.cpp index 7830eb1..3fb4d9e 100644 --- a/math/mobius.cpp +++ b/math/mobius.cpp @@ -1,4 +1,21 @@ -// Laufzeit: O(N*log(log(N))) -int mu[N+1]; mu[1] = 1; -for (int i = 1; i <= N; i++) { - for (int j = 2 * i; j <= N; j += i) mu[j] -= mu[i]; +ll mu(ll n) { // Laufzeit: O(sqrt(n)); + ll res = 1; + for (ll i = 2; i * i <= n; i++) { + if (n % i == 0) { // Optimierung: Nur Primzahlen + if (n % (i * i) == 0) return 0; + res *= -1; + n /= i; + }} + return n > 1 ? -res : res; +} + +// berechnet Möbiusfunktion. Laufzeit: O(N*log(log(N))) +vector<int> mu(n + 1, 1); +for (ll i = 2; i <= n; i++) { + if (mu[i] == 1) { + for (ll j = i; j <= n; j += i) mu[j] *= -2; + for (ll j = i*i; j <= n; j += i*i) mu[j] = 0; + } + // log2(abs(mu[i])) = number of primes + mu[i] = (mu[i] > 0) - (mu[i] < 0); +} diff --git a/math/modExp.cpp b/math/modExp.cpp index d74eca0..2329a94 100644 --- a/math/modExp.cpp +++ b/math/modExp.cpp @@ -1,7 +1,6 @@ -// Laufzeit: O(log(b)) ll powMod(ll a, ll b, ll n) { - if(b == 0) return 1; - if(b == 1) return a % n; - if(b & 1) return (powMod(a, b - 1, n) * a) % n; - else return powMod((a * a) % n, b / 2, n); + if(b == 0) return 1; + if(b == 1) return a % n; + if(b & 1) return (powMod(a, b - 1, n) * a) % n; + else return powMod((a * a) % n, b / 2, n); } diff --git a/math/modMulIterativ.cpp b/math/modMulIterativ.cpp new file mode 100644 index 0000000..611f09a --- /dev/null +++ b/math/modMulIterativ.cpp @@ -0,0 +1,9 @@ +ll mulMod(ll a, ll b, ll n) { + ll res = 0; + while (b > 0) { + if (b & 1) res = (a + res) % n; + a = (a * 2) % n; + b /= 2; + } + return res; +} diff --git a/math/modPowIterativ.cpp b/math/modPowIterativ.cpp index f06b4bd..0dc3fb1 100644 --- a/math/modPowIterativ.cpp +++ b/math/modPowIterativ.cpp @@ -1,11 +1,9 @@ -// Laufzeit: O(log (b)) ll powMod(ll a, ll b, ll n) { - if (b == 0) return 1; - ll res = 1; - while (b > 1) { - if (b & 1) res = (a * res) % n; - a = (a * a) % n; - b /= 2; - } - return (a * res) % n; + ll res = 1; + while (b > 0) { + if (b & 1) res = (a * res) % n; + a = (a * a) % n; + b /= 2; + } + return res; } diff --git a/math/modSqrt.cpp b/math/modSqrt.cpp new file mode 100644 index 0000000..367c6c7 --- /dev/null +++ b/math/modSqrt.cpp @@ -0,0 +1,23 @@ +ll sqrtMod(ll a, ll p) { + assert(powMod(a, (p + 1)/2, p) == 1); //a ist ein quadrat mod p? + if (p % 4 == 3) return powMod(a, (p + 1)/2, p); + if (p % 8 == 5) return powMod(a, (p + 3)/8, p); + ll s = p - 1; + ll r = 0; + while (s % 2 == 0) s /= 2, r++; + ll n = 2; + while (powMod(n, (p - 1)/2, p) != p - 1) n++; + ll x = powMod(a, (s + 1)/2, p); + ll b = powMod(a, s, p); + ll g = powMod(n, s, p); + while (true) { + ll t = b; + ll m = 0; + for (;m < r && t != 1; m++) t = (t * t) % p; + if (t == 1) return x; + ll gs = powMod(g, 1ll << (r - m - 1), p); + g = (gs * gs) % p; + x = (x * gs) % p; + b = (b * g) % p; + r = m; +}} diff --git a/math/multInv.cpp b/math/multInv.cpp index 4e388b8..87603f3 100644 --- a/math/multInv.cpp +++ b/math/multInv.cpp @@ -1,7 +1,5 @@ -// Laufzeit: O(log (n) + log(p)) ll multInv(ll n, ll p) { ll x, y; extendedEuclid(n, p, x, y); // Implementierung von oben. - x = ((x % p) + p) % p; - return x % p; + return ((x % p) + p) % p; } diff --git a/math/permIndex.cpp b/math/permIndex.cpp new file mode 100644 index 0000000..09ff7f7 --- /dev/null +++ b/math/permIndex.cpp @@ -0,0 +1,14 @@ +ll permIndex(vector<ll> v) { + Tree<ll> t; + reverse(all(v)); + for (ll& x : v) { + t.insert(x); + x = t.order_of_key(x); + } + ll res = 0; + for (ll i = sz(v); i > 0; i--) { + res *= i; + res += v[i - 1]; + } + return res; +} diff --git a/math/phi.cpp b/math/phi.cpp index f568bba..482a139 100644 --- a/math/phi.cpp +++ b/math/phi.cpp @@ -1,20 +1,21 @@ ll phi(ll n) { // Laufzeit: O(sqrt(n)) - // Optimierung: Falls n prim, n - 1 zurückgeben (Miller-Rabin/Sieb). - ll result = n; - for(int i = 2; i * i <= n; ++i) { - if(n % i == 0) { // Optimierung: Nur über Primzahlen iterieren. - while(n % i == 0)n /= i; - result -= result / i; - } - } - if(n > 1) result -= result / n; - return result; + // Optimierung: Falls n prim, n - 1 zurückgeben + ll result = n; + for(ll i = 2; i * i <= n; ++i) { + if(n % i == 0) { // Optimierung: Nur Primzahlen + while(n % i == 0) n /= i; + result -= result / i; + }} + if(n > 1) result -= result / n; + return result; } -// Sieb, falls alle Werte benötigt werden. Laufzeit: O(N*log(log(N))) -for (int i = 1; i <= N; i++) phi[i] = i; -for (int i = 2; i <= N; i++) if (phi[i] == i) { - for (int j = i; j <= N; j += i) { - phi[j] /= i; - phi[j] *= i - 1; +// Sieb, falls alle Werte benötigt werden. +// Laufzeit: O(N*log(log(N))) +vector<ll> phi(n + 1); +for (int i = 1; i <= n; i++) phi[i] = i; +for (int i = 2; i <= n; i++) if (phi[i] == i) { + for (int j = i; j <= n; j += i) { + phi[j] /= i; + phi[j] *= i - 1; }} diff --git a/math/piLegendre.cpp b/math/piLegendre.cpp new file mode 100644 index 0000000..f45ee85 --- /dev/null +++ b/math/piLegendre.cpp @@ -0,0 +1,23 @@ +constexpr ll cache = 500; // requires O(cache^3)
+vector<vector<ll>> memo(cache * cache, vector<ll>(cache));
+
+ll pi(ll n);
+
+ll phi(ll n, ll k) {
+ if (n <= 1 || k < 0) return 0;
+ if (n <= primes[k]) return n - 1;
+ if (n < N && primes[k] * primes[k] > n) return n - pi(n) + k;
+ bool ok = n < cache * cache;
+ if (ok && memo[n][k] > 0) return memo[n][k];
+ ll res = n/primes[k] - phi(n/primes[k], k - 1) + phi(n, k - 1);
+ if (ok) memo[n][k] = res;
+ return res;
+}
+
+ll pi(ll n) {
+ if (n < N) { // implement this as O(1) lookup for speedup!
+ return distance(primes.begin(), upper_bound(primes.begin(), primes.end(), n));
+ } else {
+ ll k = pi(sqrtl(n) + 1);
+ return n - phi(n, k) + k;
+}}
diff --git a/math/piLehmer.cpp b/math/piLehmer.cpp new file mode 100644 index 0000000..37eff6b --- /dev/null +++ b/math/piLehmer.cpp @@ -0,0 +1,52 @@ +constexpr ll cacheA = 2 * 3 * 5 * 7 * 11 * 13 * 17;
+constexpr ll cacheB = 7;
+ll memoA[cacheA + 1][cacheB + 1];
+ll memoB[cacheB + 1];
+ll memoC[N];
+
+void init() {
+ primeSieve(); // code from above
+ for (ll i = 0; i < N; i++) {
+ memoC[i] = memoC[i - 1];
+ if (isPrime(i)) memoC[i]++;
+ }
+ memoB[0] = 1;
+ for(ll i = 0; i <= cacheA; i++) memoA[i][0] = i;
+ for(ll i = 1; i <= cacheB; i++) {
+ memoB[i] = primes[i - 1] * memoB[i - 1];
+ for(ll j = 1; j <= cacheA; j++) {
+ memoA[j][i] = memoA[j][i - 1] - memoA[j /
+ primes[i - 1]][i - 1];
+}}}
+
+ll phi(ll n, ll k) {
+ if(k == 0) return n;
+ if(k <= cacheB)
+ return memoA[n % memoB[k]][k] +
+ (n / memoB[k]) * memoA[memoB[k]][k];
+ if(n <= primes[k - 1]*primes[k - 1]) return memoC[n] - k + 1;
+ if(n <= primes[k - 1]*primes[k - 1]*primes[k - 1] && n < N) {
+ ll b = memoC[(ll)sqrtl(n)];
+ ll res = memoC[n] - (b + k - 2) * (b - k + 1) / 2;
+ for(ll i = k; i < b; i++) res += memoC[n / primes[i]];
+ return res;
+ }
+ return phi(n, k - 1) - phi(n / primes[k - 1], k - 1);
+}
+
+ll pi(ll n) {
+ if (n < N) return memoC[n];
+ ll a = pi(sqrtl(sqrtl(n)));
+ ll b = pi(sqrtl(n));
+ ll c = pi(cbrtl(n));
+ ll res = phi(n, a) + (b + a - 2) * (b - a + 1) / 2;
+ for (ll i = a; i < b; i++) {
+ ll w = n / primes[i];
+ res -= pi(w);
+ if (i > c) continue;
+ ll bi = pi(sqrtl(w));
+ for (ll j = i; j < bi; j++) {
+ res -= pi(w / primes[j]) - j;
+ }}
+ return res;
+}
\ No newline at end of file diff --git a/math/polynomial.cpp b/math/polynomial.cpp new file mode 100644 index 0000000..f7d9a89 --- /dev/null +++ b/math/polynomial.cpp @@ -0,0 +1,65 @@ +struct poly { + vector<ll> data; + + poly(int deg = 0) : data(max(1, deg)) {} + poly(initializer_list<ll> _data) : data(_data) {} + + int size() const {return sz(data);} + + void trim() { + for (ll& x : data) x = (x % mod + mod) % mod; + while (size() > 1 && data.back() == 0) data.pop_back(); + } + + ll& operator[](int x) {return data[x];} + const ll& operator[](int x) const {return data[x];} + + ll operator()(int x) const { + ll res = 0; + for (int i = size() - 1; i >= 0; i--) + res = (res * x + data[i]) % mod; + return res % mod; + } + + poly& operator+=(const poly& o) { + if (size() < o.size()) data.resize(o.size()); + for (int i = 0; i < o.size(); i++) + data[i] = (data[i] + o[i]) % mod; + return *this; + } + + poly operator*(const poly& o) const { + poly res(size() + o.size() - 1); + for (int i = 0; i < size(); i++) { + for (int j = 0; j < o.size(); j++) { + res[i + j] += (data[i] * o[j]) % mod; + }} + res.trim(); + return res; + } + + //return p(x+a) + poly operator<<(ll a) const { + poly res(size()); + for (int i = size() - 1; i >= 0; i--) { + for (int j = size() - i - 1; j >= 1; j--) + res[j] = (res[j] * a + res[j - 1]) % mod; + res[0] = (res[0] * a + res[i]) % mod; + } + return res; + } + + pair<poly, poly> divmod(const poly& d) const { + int i = size() - d.size(); + poly s(i + 1), r = *this; + ll inv = multInv(d.data.back(), mod); + for (; i >= 0; i--) { + s[i] = (r.data.back() * inv) % mod; + r.data.pop_back(); + for (int j = 0; i + j < r.size(); j++) { + r[i + j] = (r.data[i + j] - s[i] * d[j]) % mod; + }} + s.trim(); r.trim(); + return {s, r}; + } +}; diff --git a/math/primeSieve.cpp b/math/primeSieve.cpp index 286aa1d..68f7fcb 100644 --- a/math/primeSieve.cpp +++ b/math/primeSieve.cpp @@ -1,22 +1,17 @@ -// Laufzeit: O(n * log log n) -// Kann erweitert werden: Für jede Zahl den kleinsten Primfaktor. -// Dabei vorsicht: Nicht kleinere Faktoren überschreiben. -#define N 100000000 // Bis 10^8 in unter 64MB Speicher. +constexpr ll N = 100000000; bitset<N / 2> isNotPrime; +vector<ll> primes = {2}; -inline bool isPrime(int x) { // Diese Methode zum Lookup verwenden. - if (x < 2) return false; - else if (x == 2) return true; - else if (!(x & 1)) return false; - else return !isNotPrime[x / 2]; +bool isPrime(ll x) { + if (x < 2 || x % 2 == 0) return x == 2; + else return !isNotPrime[x / 2]; } -inline int primeSieve() { // Rückgabe: Anzahl der Primzahlen < N. - int counter = 1; // Die 2, die sonst vergessen würde. - for (int i = 3; i < N; i += 2) { - if (!isNotPrime[i / 2]) { - for (int j = i * i; j < N; j+= 2 * i) isNotPrime[j / 2] = 1; - counter++; - }} - return counter; -} +void primeSieve() { + // i * i < N is enough for isPrime + for (ll i = 3; i < N; i += 2) { + if (!isNotPrime[i / 2]) { + primes.push_back(i); + for (ll j = i * i; j < N; j+= 2 * i) { + isNotPrime[j / 2] = 1; +}}}} diff --git a/math/primes.cpp b/math/primes.cpp deleted file mode 100644 index 79e0001..0000000 --- a/math/primes.cpp +++ /dev/null @@ -1,39 +0,0 @@ -bool isPrime(ll n) { // Miller Rabin Primzahltest. O(log n) - if(n == 2) return true; - if(n < 2 || n % 2 == 0) return false; - ll d = n - 1, j = 0; - while(d % 2 == 0) d >>= 1, j++; - for(int a = 2; a <= min((ll)37, n - 1); a++) { - ll v = powMod(a, d, n); // Implementierung von oben. - if(v == 1 || v == n - 1) continue; - for(int i = 1; i <= j; i++) { - v = (v * v) % n; - if(v == n - 1 || v <= 1) break; - } - if(v != n - 1) return false; - } - return true; -} - -ll rho(ll n) { // Findet Faktor < n, nicht unbedingt prim. - if (~n & 1) return 2; - ll c = rand() % n, x = rand() % n, y = x, d = 1; - while (d == 1) { - x = ((x * x) % n + c) % n; - y = ((y * y) % n + c) % n; - y = ((y * y) % n + c) % n; - d = gcd(abs(x - y), n); // Implementierung von oben. - } - return d == n ? rho(n) : d; -} - -void factor(ll n, map<ll, int> &facts) { - if (n == 1) return; - if (isPrime(n)) { - facts[n]++; - return; - } - ll f = rho(n); - factor(n / f, facts); - factor(f, facts); -} diff --git a/math/primitiveRoot.cpp b/math/primitiveRoot.cpp index 3ad828d..36a9367 100644 --- a/math/primitiveRoot.cpp +++ b/math/primitiveRoot.cpp @@ -1,23 +1,23 @@ -// Ist g Primitivwurzel modulo p. Teste zufällige g, um eine zu finden. -bool is_primitive(ll g, ll p) { - map<ll, int> facs; - factor(p - 1, facs); - for (auto &f : facs) - if (1 == powMod(g, (p - 1) / f.first, p)) return false; - return true; +bool isPrimitive(ll g, ll n, ll phi, map<ll, int> phiFacs) { + if (g == 1) return n == 2; + for (auto& f : phiFacs) + if (1 == powMod(g, phi / f.first, n)) return false; + return true; } -// Alternativ: Generator zum Finden. -1 falls keine existiert. -ll generator (ll p) { // Laufzeit: O(ans*log(phi(n))*log(n)) - map<ll, int> facs; - factor(n, facs); - ll phi = phi(p), n = phi; - - for (ll res = 2; res <= p; res++) { - bool ok = true; - for (auto &f : facs) - ok &= powMod(res, phi / f.first, p) != 1; - if (ok) return res; - } - return -1; +bool isPrimitive(ll g, ll n) { + ll phin = phi(n); //isPrime(n) => phi(n) = n - 1 + map<ll, int> phiFacs; + factor(phin, phiFacs); + return isPrimitive(g, n, phin, phiFacs); } + +ll findPrimitive(ll n) { + ll phin = phi(n); //isPrime(n) => phi(n) = n - 1 + map<ll, int> phiFacs; + factor(phin, phiFacs); + //auch zufällige Reihenfolge möglich! + for (ll res = 1; res < n; res++) + if (isPrimitive(res, n, phin, phiFacs)) return res; + return -1; +}
\ No newline at end of file diff --git a/math/rho.cpp b/math/rho.cpp new file mode 100644 index 0000000..bd30902 --- /dev/null +++ b/math/rho.cpp @@ -0,0 +1,22 @@ +ll rho(ll n) { // Findet Faktor < n, nicht unbedingt prim. + if (n % 2 == 0) return 2; + ll c = rand() % n, x = rand() % n, y = x, d = 1; + // mulmod or int128 + auto f = [&](ll x){return ((x * x) % n + c) % n;}; + while (d == 1) { + x = f(x); y = f(f(y)); + d = gcd(abs(x - y), n); + } + return d == n ? rho(n) : d; +} + +void factor(ll n, map<ll, int>& facts) { + if (n == 1) return; + if (isPrime(n)) { + facts[n]++; + return; + } + ll f = rho(n); + factor(n / f, facts); + factor(f, facts); +} diff --git a/math/simpson.cpp b/math/simpson.cpp index dd887e2..a99b911 100644 --- a/math/simpson.cpp +++ b/math/simpson.cpp @@ -1,12 +1,12 @@ -double f(double x) { return x; } +double f(double x) {return x;} double simps(double a, double b) { - return (f(a) + 4.0 * f((a + b) / 2.0) + f(b)) * (b - a) / 6.0; + return (f(a) + 4.0 * f((a + b) / 2.0) + f(b)) * (b - a) / 6.0; } double integrate(double a, double b) { - double m = (a + b) / 2.0; - double l = simps(a, m), r = simps(m, b), tot = simps(a, b); - if (abs(l + r - tot) < EPSILON) return tot; - return integrate(a, m) + integrate(m, b); + double m = (a + b) / 2.0; + double l = simps(a, m), r = simps(m, b), tot = simps(a, b); + if (abs(l + r - tot) < EPS) return tot; + return integrate(a, m) + integrate(m, b); } diff --git a/math/spheres.cpp b/math/spheres.cpp deleted file mode 100644 index 324eb00..0000000 --- a/math/spheres.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Great Cirlce Distance mit Längen- und Breitengrad. -double gcDist( - double pLat, double pLon, double qLat, double qLon, double radius) { - pLat *= PI / 180; pLon *= PI / 180; qLat *= PI / 180; qLon *= PI / 180; - return radius * acos(cos(pLat) * cos(pLon) * cos(qLat) * cos(qLon) + - cos(pLat) * sin(pLon) * cos(qLat) * sin(qLon) + - sin(pLat) * sin(qLat)); -} - -// Great Cirlce Distance mit kartesischen Koordinaten. -double gcDist(point p, point q) { - return acos(p.x * q.x + p.y * q.y + p.z * q.z); -} - -// 3D Punkt in kartesischen Koordinaten. -struct point{ - double x, y, z; - point() {} - point(double x, double y, double z) : x(x), y(y), z(z) {} - point(double lat, double lon) { - lat *= PI / 180.0; lon *= PI / 180.0; - x = cos(lat) * sin(lon); y = cos(lat) * cos(lon); z = sin(lat); - } -}; diff --git a/math/squfof.cpp b/math/squfof.cpp new file mode 100644 index 0000000..8a11a77 --- /dev/null +++ b/math/squfof.cpp @@ -0,0 +1,89 @@ +using lll = __int128;
+
+constexpr lll multipliers[] = {1, 3, 5, 7,
+ 11, 3*5, 3*7, 3*11,
+ 5*7, 5*11, 7*11,
+ 3*5*7, 3*5*11, 3*7*11,
+ 5*7*11, 3*5*7*11};
+
+lll root(lll x) {
+ lll r = sqrtl(x);
+ while(r*r < x) r++;
+ while(r*r > x) r--;
+ return r;
+}
+
+lll croot(lll x) {
+ lll r = cbrtl(x);
+ while(r*r*r < x) r++;
+ while(r*r*r > x) r--;
+ return r;
+}
+
+lll squfof(lll N) {
+ lll s = croot(N);
+ if (s*s*s == N) return s;
+ s = root(N);
+ if (s*s == N) return s;
+ for (lll k : multipliers) {
+ lll D = k * N;
+ lll Po, P, Pprev, q, b, r, i;
+ Po = Pprev = P = root(D);
+ lll Qprev = 1;
+ lll Q = D - Po*Po;
+ lll L = 2 * root(2 * s);
+ lll B = 3 * L;
+ for (i = 2; i < B; i++) {
+ b = (Po + P) / Q;
+ P = b*Q - P;
+ q = Q;
+ Q = Qprev + b * (Pprev - P);
+ r = root(Q);
+ if (!(i & 1) && r*r == Q) break;
+ Qprev = q;
+ Pprev = P;
+ }
+ if (i >= B) continue;
+ b = (Po - P) / r;
+ Pprev = P = b*r + P;
+ Qprev = r;
+ Q = (D-Pprev*Pprev)/Qprev;
+ i = 0;
+ do {
+ b = (Po + P) / Q;
+ Pprev = P;
+ P = b*Q - P;
+ q = Q;
+ Q = Qprev + b * (Pprev - P);
+ Qprev = q;
+ i++;
+ } while(P != Pprev);
+ r = gcd(N, Qprev);
+ if (r != 1 && r != N) return r;
+ }
+ exit(1);//try fallback to pollard rho
+}
+
+constexpr lll trialLim = 5000;
+
+void factor(lll n, map<lll, int>& facts) {
+ for (lll i = 2; i * i <= n && i <= trialLim; i++) {
+ while (n % i == 0) {
+ facts[i]++;
+ n /= i;
+ }}
+ if (n > 1 && n < trialLim * trialLim) {
+ facts[n]++;
+ } else {
+ vector<lll> todo = {n};
+ while (!todo.empty()) {
+ lll c = todo.back();
+ todo.pop_back();
+ if (c == 1) continue;
+ if (isPrime(c)) {
+ facts[c]++;
+ } else {
+ lll d = squfof(c);
+ todo.push_back(d);
+ todo.push_back(c / d);
+}}}}
diff --git a/math/tables.tex b/math/tables.tex new file mode 100644 index 0000000..53f3758 --- /dev/null +++ b/math/tables.tex @@ -0,0 +1,18 @@ +\enlargethispage{0.2cm} +\begin{multicols*}{2} + \input{math/tables/binom} + \vfill + \input{math/tables/composite} + \vfill + \input{math/tables/platonic} + \vfill + \input{math/tables/series} + + \columnbreak + + \input{math/tables/probability} + \vfill + \input{math/tables/stuff} + \vfill + \input{math/tables/nim} +\end{multicols*} diff --git a/math/tables/binom.tex b/math/tables/binom.tex new file mode 100644 index 0000000..878a6b0 --- /dev/null +++ b/math/tables/binom.tex @@ -0,0 +1,28 @@ +\begin{tabularx}{\linewidth}{|XXXX|} + \hline + \multicolumn{4}{|c|}{Binomialkoeffizienten} \\ + \hline + \multicolumn{4}{|c|}{ + $\frac{n!}{k!(n - k)!} \hfill=\hfill + \binom{n}{k} \hfill=\hfill + \binom{n}{n - k} \hfill=\hfill + \frac{n}{k}\binom{n - 1}{k - 1} \hfill=\hfill + \frac{n-k+1}{k}\binom{n}{k - 1} \hfill=\hfill + \binom{n - 1}{k} + \binom{n - 1}{k - 1} \hfill=\hfill + (-1)^k \binom{k - n - 1}{k} \hfill\approx\hfill + 2^{n} \cdot \frac{2}{\sqrt{2\pi n}}\cdot\exp\left(-\frac{2(x - \frac{n}{2})^2}{n}\right)$ + } \\ + \grayhline + + $\sum\limits_{k = 0}^n \binom{n}{k} = 2^n$ & + $\sum\limits_{k = 0}^n \binom{k}{m} = \binom{n + 1}{m + 1}$ & + $\sum\limits_{i = 0}^n \binom{n}{i}^2 = \binom{2n}{n}$ & + $\sum\limits_{k = 0}^n\binom{r + k}{k} = \binom{r + n + 1}{n}$\\ + + $\binom{n}{m}\binom{m}{k} = \binom{n}{k}\binom{n - k}{m - k}$ & + $\sum\limits_{k = 0}^n \binom{r}{k}\binom{s}{n - k} = \binom{r + s}{n}$ & + \multicolumn{2}{l|}{ + $\sum\limits_{i = 1}^n \binom{n}{i} F_i = F_{2n} \quad F_n = n\text{-th Fib.}$ + }\\ + \hline +\end{tabularx} diff --git a/math/tables/composite.tex b/math/tables/composite.tex new file mode 100644 index 0000000..b4c8294 --- /dev/null +++ b/math/tables/composite.tex @@ -0,0 +1,26 @@ +\begin{tabularx}{\linewidth}{|r|r|r|CICICICICICICICICICICIC|} + \hline + \multicolumn{15}{|c|}{Highly Composite Numbers} \\ + \hline + $10^x$ & Zahl & Teiler & 2 & 3 & 5 & 7 & 11 & 13 & 17 & 19 & 23 & 29 & 31 & 37 \\ + \hline + 1 & 6 & 4 & 1 & 1 & & & & & & & & & & \\ + 2 & 60 & 12 & 2 & 1 & 1 & & & & & & & & & \\ + 3 & 840 & 32 & 3 & 1 & 1 & 1 & & & & & & & &\\ + 4 & 7560 & 64 & 3 & 3 & 1 & 1 & & & & & & & & \\ + 5 & 83160 & 128 & 3 & 3 & 1 & 1 & 1 & & & & & & & \\ + 6 & 720720 & 240 & 4 & 2 & 1 & 1 & 1 & 1 & & & & & & \\ + 7 & 8648640 & 448 & 6 & 3 & 1 & 1 & 1 & 1 & & & & & & \\ + 8 & 73513440 & 768 & 5 & 3 & 1 & 1 & 1 & 1 & 1 & & & & & \\ + 9 & 735134400 & 1344 & 6 & 3 & 2 & 1 & 1 & 1 & 1 & & & & & \\ + 10 & 6983776800 & 2304 & 5 & 3 & 2 & 1 & 1 & 1 & 1 & 1 & & & & \\ + 11 & 97772875200 & 4032 & 6 & 3 & 2 & 2 & 1 & 1 & 1 & 1 & & & & \\ + 12 & 963761198400 & 6720 & 6 & 4 & 2 & 1 & 1 & 1 & 1 & 1 & 1 & & & \\ + 13 & 9316358251200 & 10752 & 6 & 3 & 2 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & & \\ + 14 & 97821761637600 & 17280 & 5 & 4 & 2 & 2 & 1 & 1 & 1 & 1 & 1 & 1 & & \\ + 15 & 866421317361600 & 26880 & 6 & 4 & 2 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & \\ + 16 & 8086598962041600 & 41472 & 8 & 3 & 2 & 2 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & \\ + 17 & 74801040398884800 & 64512 & 6 & 3 & 2 & 2 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\ + 18 & 897612484786617600 & 103680 & 8 & 4 & 2 & 2 & 1 & 1 & 1 & 1 & 1 & 1 & 1 & 1 \\ + \hline +\end{tabularx} diff --git a/math/tables/nim.tex b/math/tables/nim.tex new file mode 100644 index 0000000..75585f4 --- /dev/null +++ b/math/tables/nim.tex @@ -0,0 +1,96 @@ +\begin{tabularx}{\linewidth}{|p{0.37\linewidth}|X|} + \hline + \multicolumn{2}{|c|}{Nim-Spiele (\ding{182} letzter gewinnt (normal), \ding{183} letzter verliert)} \\ + \hline + Beschreibung & + Strategie \\ + \hline + + $M = [\mathit{pile}_i]$\newline + $[x] := \{1, \ldots, x\}$& + $\mathit{SG} = \oplus_{i = 1}^n \mathit{pile}_i$\newline + \ding{182} Nimm von einem Stapel, sodass $\mathit{SG}$ $0$ wird.\newline + \ding{183} Genauso. + Außer: Bleiben nur noch Stapel der Größe $1$, erzeuge ungerade Anzahl solcher Stapel.\\ + \hline + + $M = \{a^m \mid m \geq 0\}$ & + $a$ ungerade: $\mathit{SG}_n = n \% 2$\newline + $a$ gerade:\newline + $\mathit{SG}_n = 2$, falls $n \equiv a \bmod (a + 1) $\newline + $\mathit{SG}_n = n \% (a + 1) \% 2$, sonst.\\ + \hline + + $M_{\text{\ding{172}}} = \left[\frac{\mathit{pile}_i}{2}\right]$\newline + $M_{\text{\ding{173}}} = + \left\{\left\lceil\frac{\mathit{pile}_i}{2}\right\rceil,~ + \mathit{pile}_i\right\}$ & + \ding{172} + $\mathit{SG}_{2n} = n$, + $\mathit{SG}_{2n+1} = \mathit{SG}_n$\newline + \ding{173} + $\mathit{SG}_0 = 0$, + $\mathit{SG}_n = [\log_2 n] + 1$ \\ + \hline + + $M_{\text{\ding{172}}} = \text{Teiler von $\mathit{pile}_i$}$\newline + $M_{\text{\ding{173}}} = \text{echte Teiler von $\mathit{pile}_i$}$ & + \ding{172} + $\mathit{SG}_0 = 0$, + $\mathit{SG}_n = \mathit{SG}_{\text{\ding{173},n}} + 1$\newline + \ding{173} + $\mathit{ST}_1 = 0$, + $\mathit{SG}_n = \text{\#Nullen am Ende von $n_{bin}$}$\\ + \hline + + $M_{\text{\ding{172}}} = [k]$\newline + $M_{\text{\ding{173}}} = S$, ($S$ endlich)\newline + $M_{\text{\ding{174}}} = S \cup \{\mathit{pile}_i\}$ & + $\mathit{SG}_{\text{\ding{172}}, n} = n \bmod (k + 1)$\newline + \ding{182} Niederlage bei $\mathit{SG} = 0$\newline + \ding{183} Niederlage bei $\mathit{SG} = 1$\newline + $\mathit{SG}_{\text{\ding{174}}, n} = \mathit{SG}_{\text{\ding{173}}, n} + 1$\\ + \hline + + \multicolumn{2}{|l|}{ + Für jedes endliche $M$ ist $\mathit{SG}$ eines Stapels irgendwann periodisch. + } \\ + \hline + + \textsc{Moore}'s Nim:\newline + Beliebige Zahl von maximal $k$ Stapeln. & + \ding{182} + Schreibe $\mathit{pile}_i$ binär. + Addiere ohne Übertrag zur Basis $k + 1$. + Niederlage, falls Ergebnis gleich 0.\newline + \ding{183} + Wenn alle Stapel $1$ sind: + Niederlage, wenn $n \equiv 1 \bmod (k + 1)$. + Sonst wie in \ding{182}.\\ + \hline + + Staircase Nim:\newline + $n$ Stapel in einer Reihe. + Beliebige Zahl von Stapel $i$ nach Stapel $i-1$. & + Niederlage, wenn Nim der ungeraden Spiele verloren ist:\newline + $\oplus_{i = 0}^{(n - 1) / 2} \mathit{pile}_{2i + 1} = 0$\\ + \hline + + \textsc{Lasker}'s Nim:\newline + Zwei mögliche Züge:\newline + 1) Nehme beliebige Zahl.\newline + 2) Teile Stapel in zwei Stapel (ohne Entnahme).& + $\mathit{SG}_n = n$, falls $n \equiv 1,2 \bmod 4$\newline + $\mathit{SG}_n = n + 1$, falls $n \equiv 3 \bmod 4$\newline + $\mathit{SG}_n = n - 1$, falls $n \equiv 0 \bmod 4$\\ + \hline + + \textsc{Kayles}' Nim:\newline + Zwei mögliche Züge:\newline + 1) Nehme beliebige Zahl.\newline + 2) Teile Stapel in zwei Stapel (mit Entnahme).& + Berechne $\mathit{SG}_n$ für kleine $n$ rekursiv.\newline + $n \in [72,83]: \quad 4, 1, 2, 8, 1, 4, 7, 2, 1, 8, 2, 7$\newline + Periode ab $n = 72$ der Länge $12$.\\ + \hline +\end{tabularx}
\ No newline at end of file diff --git a/math/tables/numbers.tex b/math/tables/numbers.tex new file mode 100644 index 0000000..1dc9f38 --- /dev/null +++ b/math/tables/numbers.tex @@ -0,0 +1,59 @@ +\begin{expandtable} +\begin{tabularx}{\linewidth}{|l|X|} + \hline + \multicolumn{2}{|c|}{Berühmte Zahlen} \\ + \hline + \textsc{Fibonacci} & + $f(0) = 0 \quad + f(1) = 1 \quad + f(n+2) = f(n+1) + f(n)$ \\ + \grayhline + + \textsc{Catalan} & + $C_0 = 1 \qquad + C_n = \sum\limits_{k = 0}^{n - 1} C_kC_{n - 1 - k} = + \frac{1}{n + 1}\binom{2n}{n} = \frac{2(2n - 1)}{n+1} \cdot C_{n-1}$ \\ + \grayhline + + \textsc{Euler} I & + $\eulerI{n}{0} = \eulerI{n}{n-1} = 1 \qquad + \eulerI{n}{k} = (k+1) \eulerI{n-1}{k} + (n-k) \eulerI{n-1}{k-1} $ \\ + \grayhline + + \textsc{Euler} II & + $\eulerII{n}{0} = 1 \quad + \eulerII{n}{n} = 0 \quad$\\ + & $\eulerII{n}{k} = (k+1) \eulerII{n-1}{k} + (2n-k-1) \eulerII{n-1}{k-1}$ \\ + \grayhline + + \textsc{Stirling} I & + $\stirlingI{0}{0} = 1 \qquad + \stirlingI{n}{0} = \stirlingI{0}{n} = 0 \qquad + \stirlingI{n}{k} = \stirlingI{n-1}{k-1} + (n-1) \stirlingI{n-1}{k}$ \\ + \grayhline + + \textsc{Stirling} II & + $\stirlingII{n}{1} = \stirlingII{n}{n} = 1 \qquad + \stirlingII{n}{k} = k \stirlingII{n-1}{k} + \stirlingII{n-1}{k-1} = + \frac{1}{k!} \sum\limits_{j=0}^{k} (-1)^{k-j}\binom{k}{j}j^n$\\ + \grayhline + + \textsc{Bell} & + $B_1 = 1 \qquad + B_n = \sum\limits_{k = 0}^{n - 1} B_k\binom{n-1}{k} + = \sum\limits_{k = 0}^{n}\stirlingII{n}{k}$\\ + \grayhline + + \textsc{Partitions} & + $p(0,0) = 1 \quad + p(n,k) = 0 \text{ für } k > n \text{ oder } n \leq 0 \text{ oder } k \leq 0$ \\ + & $p(n,k) = p(n-k,k) + p(n-1,k-1)$\\ + \grayhline + + \textsc{Partitions} & + $f(0) = 1 \quad f(n) = 0~(n < 0)$ \\ + & $f(n)=\sum\limits_{k=1}^\infty(-1)^{k-1}f(n - \frac{k(3k+1)}{2})+\sum\limits_{k=1}^\infty(-1)^{k-1}f(n - \frac{k(3k-1)}{2})$\\ + + \hline +\end{tabularx} +\end{expandtable} diff --git a/math/tables/platonic.tex b/math/tables/platonic.tex new file mode 100644 index 0000000..f4ee554 --- /dev/null +++ b/math/tables/platonic.tex @@ -0,0 +1,39 @@ +\begin{tabularx}{\linewidth}{|X|CCCX|} + \hline + \multicolumn{5}{|c|}{Platonische Körper} \\ + \hline + Übersicht & Seiten & Ecken & Kanten & dual zu \\ + \hline + Tetraeder & 4 & 4 & 6 & Tetraeder \\ + Würfel/Hexaeder & 6 & 8 & 12 & Oktaeder \\ + Oktaeder & 8 & 6 & 12 & Würfel/Hexaeder\\ + Dodekaeder & 12 & 20 & 30 & Ikosaeder \\ + Ikosaeder & 20 & 12 & 30 & Dodekaeder \\ + \hline + \multicolumn{5}{|c|}{Färbungen mit maximal $n$ Farben (bis auf Isomorphie)} \\ + \hline + \multicolumn{3}{|l}{Ecken vom Oktaeder/Seiten vom Würfel} & + \multicolumn{2}{l|}{$(n^6 + 3n^4 + 12n^3 + 8n^2)/24$} \\ + + \multicolumn{3}{|l}{Ecken vom Würfel/Seiten vom Oktaeder} & + \multicolumn{2}{l|}{$(n^8 + 17n^4 + 6n^2)/24$} \\ + + \multicolumn{3}{|l}{Kanten vom Würfel/Oktaeder} & + \multicolumn{2}{l|}{$(n^{12} + 6n^7 + 3n^6 + 8n^4 + 6n^3)/24$} \\ + + \multicolumn{3}{|l}{Ecken/Seiten vom Tetraeder} & + \multicolumn{2}{l|}{$(n^4 + 11n^2)/12$} \\ + + \multicolumn{3}{|l}{Kanten vom Tetraeder} & + \multicolumn{2}{l|}{$(n^6 + 3n^4 + 8n^2)/12$} \\ + + \multicolumn{3}{|l}{Ecken vom Ikosaeder/Seiten vom Dodekaeder} & + \multicolumn{2}{l|}{$(n^{12} + 15n^6 + 44n^4)/60$} \\ + + \multicolumn{3}{|l}{Ecken vom Dodekaeder/Seiten vom Ikosaeder} & + \multicolumn{2}{l|}{$(n^{20} + 15n^{10} + 20n^8 + 24n^4)/60$} \\ + + \multicolumn{3}{|l}{Kanten vom Dodekaeder/Ikosaeder (evtl. falsch)} & + \multicolumn{2}{l|}{$(n^{30} + 15n^{16} + 20n^{10} + 24n^6)/60$} \\ + \hline +\end{tabularx} diff --git a/math/tables/probability.tex b/math/tables/probability.tex new file mode 100644 index 0000000..4f72707 --- /dev/null +++ b/math/tables/probability.tex @@ -0,0 +1,27 @@ +\begin{tabularx}{\linewidth}{|LICIR|} + \hline + \multicolumn{3}{|c|}{ + Wahrscheinlichkeitstheorie ($A,B$ Ereignisse und $X,Y$ Variablen) + } \\ + \hline + $\E(X + Y) = \E(X) + \E(Y)$ & + $\E(\alpha X) = \alpha \E(X)$ & + $X, Y$ unabh. $\Leftrightarrow \E(XY) = \E(X) \cdot \E(Y)$\\ + + $\Pr[A \vert B] = \frac{\Pr[A \land B]}{\Pr[B]}$ & + $\Pr[A \land B] = \Pr[A] \cdot \Pr[B]$ & + $\Pr[A \lor B] = \Pr[A] + \Pr[B] - \Pr[A \land B]$ \\ + \hline +\end{tabularx} +\vfill +\begin{tabularx}{\linewidth}{|Xlr|lrX|} + \hline + \multicolumn{6}{|c|}{\textsc{Bertrand}'s Ballot Theorem (Kandidaten $A$ und $B$, $k \in \mathbb{N}$)} \\ + \hline + & $\#A > k\#B$ & $Pr = \frac{a - kb}{a + b}$ & + $\#B - \#A \leq k$ & $Pr = 1 - \frac{a!b!}{(a + k + 1)!(b - k - 1)!}$ & \\ + + & $\#A \geq k\#B$ & $Pr = \frac{a + 1 - kb}{a + 1}$ & + $\#A \geq \#B + k$ & $Num = \frac{a - k + 1 - b}{a - k + 1} \binom{a + b - k}{b}$ & \\ + \hline +\end{tabularx} diff --git a/math/tables/series.tex b/math/tables/series.tex new file mode 100644 index 0000000..13af68f --- /dev/null +++ b/math/tables/series.tex @@ -0,0 +1,33 @@ +\begin{tabularx}{\linewidth}{|XIXIXIX|} + \hline + \multicolumn{4}{|c|}{Reihen} \\ + \hline + $\sum\limits_{i = 1}^n i = \frac{n(n+1)}{2}$ & + $\sum\limits_{i = 1}^n i^2 = \frac{n(n + 1)(2n + 1)}{6}$ & + $\sum\limits_{i = 1}^n i^3 = \frac{n^2 (n + 1)^2}{4}$ & + $H_n = \sum\limits_{i = 1}^n \frac{1}{i}$ \\ + \grayhline + + $\sum\limits_{i = 0}^n c^i = \frac{c^{n + 1} - 1}{c - 1} \quad c \neq 1$ & + $\sum\limits_{i = 0}^\infty c^i = \frac{1}{1 - c} \quad \vert c \vert < 1$ & + $\sum\limits_{i = 1}^\infty c^i = \frac{c}{1 - c} \quad \vert c \vert < 1$ & + $\sum\limits_{i = 0}^\infty ic^i = \frac{c}{(1 - c)^2} \quad \vert c \vert < 1$ \\ + \grayhline + + \multicolumn{2}{|lI}{ + $\sum\limits_{i = 0}^n ic^i = \frac{nc^{n + 2} - (n + 1)c^{n + 1} + c}{(c - 1)^2} \quad c \neq 1$ + } & + \multicolumn{2}{l|}{ + $\sum\limits_{i = 1}^n iH_i = \frac{n(n + 1)}{2}H_n - \frac{n(n - 1)}{4}$ + } \\ + \grayhline + + \multicolumn{2}{|lI}{ + $\sum\limits_{i = 1}^n H_i = (n + 1)H_n - n$ + } & + \multicolumn{2}{l|}{ + $\sum\limits_{i = 1}^n \binom{i}{m}H_i = + \binom{n + 1}{m + 1} \left(H_{n + 1} - \frac{1}{m + 1}\right)$ + } \\ + \hline +\end{tabularx}
\ No newline at end of file diff --git a/math/tables/stuff.tex b/math/tables/stuff.tex new file mode 100644 index 0000000..5b5093e --- /dev/null +++ b/math/tables/stuff.tex @@ -0,0 +1,32 @@ +\begin{tabularx}{\linewidth}{|ll|} + \hline + \multicolumn{2}{|C|}{Verschiedenes} \\ + \hline + Türme von Hanoi, minimale Schirttzahl: & + $T_n = 2^n - 1$ \\ + + \#Regionen zwischen $n$ Geraden & + $\frac{n\left(n + 1\right)}{2} + 1$ \\ + + \#abgeschlossene Regionen zwischen $n$ Geraden & + $\frac{n^2 - 3n + 2}{2}$ \\ + + \#markierte, gewurzelte Bäume & + $n^{n-1}$ \\ + + \#markierte, nicht gewurzelte Bäume & + $n^{n-2}$ \\ + + \#Wälder mit $k$ gewurzelten Bäumen & + $\frac{k}{n}\binom{n}{k}n^{n-k}$ \\ + + \#Wälder mit $k$ gewurzelten Bäumen mit vorgegebenen Wurzelknoten& + $\frac{k}{n}n^{n-k}$ \\ + + Dearangements & + $!n = (n - 1)(!(n - 1) + !(n - 2)) = \left\lfloor\frac{n!}{e} + \frac{1}{2}\right\rfloor$ \\ + & + $\lim\limits_{n \to \infty} \frac{!n}{n!} = \frac{1}{e}$ \\ + \hline +\end{tabularx} + diff --git a/math/tables/twelvefold.tex b/math/tables/twelvefold.tex new file mode 100644 index 0000000..7f7e27a --- /dev/null +++ b/math/tables/twelvefold.tex @@ -0,0 +1,32 @@ +\begin{expandtable} +\begin{tabularx}{\linewidth}{|C|CICICIC|} + \hline + Bälle & identisch & verschieden & identisch & verschieden \\ + Boxen & identisch & identisch & verschieden & verschieden \\ + \hline + -- & + $p_k(n + k)$ & + $\sum\limits_{i = 0}^k \stirlingII{n}{i}$ & + $\binom{n + k - 1}{k - 1}$ & + $k^n$ \\ + \grayhline + + \makecell{Bälle pro\\Box $\geq 1$} & + $p_k(n)$ & + $\stirlingII{n}{k}$ & + $\binom{n - 1}{k - 1}$ & + $k! \stirlingII{n}{k}$ \\ + \grayhline + + \makecell{Bälle pro\\Box $\leq 1$} & + $[n \leq k]$ & + $[n \leq k]$ & + $\binom{k}{n}$ & + $n! \binom{k}{n}$ \\ + \hline + \multicolumn{5}{|l|}{ + $[\text{Bedingung}]$: \lstinline{return Bedingung ? 1 : 0;} + } \\ + \hline +\end{tabularx} +\end{expandtable}
\ No newline at end of file diff --git a/math/transforms/all.cpp b/math/transforms/all.cpp new file mode 100644 index 0000000..4f4d83b --- /dev/null +++ b/math/transforms/all.cpp @@ -0,0 +1,62 @@ +/*constexpr ll mod = 998244353; @\hl{NTT only}@ +constexpr ll root = 3;*/ + +using cplx = complex<double>; + +@\hl{NTT, xor, or, and}@ +//void fft(vector<ll> &a, bool inverse = 0) { +void fft(vector<cplx>& a, bool inverse = 0) { + int n = a.size(); + for (int i = 0, j = 1; j < n - 1; ++j) { + for (int k = n >> 1; k > (i ^= k); k >>= 1); + if (j < i) swap(a[i], a[j]); + } + for (int s = 1; s < n; s *= 2) { + /*ll ws = powMod(root, (mod - 1) / s >> 1, mod); @\hl{NTT only}@ + if (inverse) ws = powMod(ws, mod - 2, mod);*/ + double angle = PI / s * (inverse ? -1 : 1); + cplx ws(cos(angle), sin(angle)); + for (int j = 0; j < n; j+= 2 * s) { + //ll w = 1; @\hl{NTT only}@ + cplx w = 1; + for (int k = 0; k < s; k++) { + /*ll u = a[j + k], t = a[j + s + k] * w; @\hl{NTT only}@ + t %= mod; + a[j + k] = (u + t) % mod; + a[j + s + k] = (u - t + mod) % mod; + w *= ws; + w %= mod;*/ + /*ll u = a[j + k], t = a[j + s + k]; @\hl{xor only}@ + a[j + k] = u + t; + a[j + s + k] = u - t;*/ + /*if (!inverse) { @\hl{or only}@ + a[j + k] = u + t; + a[j + s + k] = u; + } else { + a[j + k] = t; + a[j + s + k] = u - t; + }*/ + /*if (!inverse) { @\hl{and only}@ + a[j + k] = t; + a[j + s + k] = u + t; + } else { + a[j + k] = t - u; + a[j + s + k] = u; + }*/ + cplx u = a[j + k], t = a[j + s + k] * w; + a[j + k] = u + t; + a[j + s + k] = u - t; + if (inverse) a[j + k] /= 2, a[j + s + k] /= 2; + w *= ws; + }}} + /*if (inverse) { @\hl{NTT only}@ + ll div = powMod(n, mod - 2, mod); + for (ll i = 0; i < n; i++) { + a[i] *= div; + a[i] %= mod; + }}*/ + /*if (inverse) { @\hl{xor only}@ + for (ll i = 0; i < n; i++) { + a[i] /= n; + }}*/ +} diff --git a/math/transforms/andTransform.cpp b/math/transforms/andTransform.cpp new file mode 100644 index 0000000..cdc7b22 --- /dev/null +++ b/math/transforms/andTransform.cpp @@ -0,0 +1,17 @@ +void fft(vector<cplx>& a, bool inverse = 0) { + int n = sz(a); + for (int i = 0, j = 1; j < n - 1; ++j) { + for (int k = n >> 1; k > (i ^= k); k >>= 1); + if (j < i) swap(a[i], a[j]); + } + for (int s = 1; s < n; s *= 2) { + for (int j = 0; j < n; j+= 2 * s) { + for (int k = 0; k < s; k++) { + ll u = a[j + k], t = a[j + s + k]; + if (!inverse) { + a[j + k] = t; + a[j + s + k] = u + t; + } else { + a[j + k] = t - u; + a[j + s + k] = u; +}}}}
\ No newline at end of file diff --git a/math/transforms/fft.cpp b/math/transforms/fft.cpp new file mode 100644 index 0000000..4540ed8 --- /dev/null +++ b/math/transforms/fft.cpp @@ -0,0 +1,20 @@ +using cplx = complex<double>; // Eigene Implementierung ist schneller. + +void fft(vector<cplx>& a, bool inverse = 0) { + int n = sz(a); + for (int i = 0, j = 1; j < n - 1; ++j) { + for (int k = n >> 1; k > (i ^= k); k >>= 1); + if (j < i) swap(a[i], a[j]); + } + for (int s = 1; s < n; s *= 2) { + double angle = PI / s * (inverse ? -1 : 1); + cplx ws(cos(angle), sin(angle)); + for (int j = 0; j < n; j+= 2 * s) { + cplx w = 1; + for (int k = 0; k < s; k++) { + cplx u = a[j + k], t = a[j + s + k] * w; + a[j + k] = u + t; + a[j + s + k] = u - t; + if (inverse) a[j + k] /= 2, a[j + s + k] /= 2; + w *= ws; +}}}} diff --git a/math/transforms/fftMul.cpp b/math/transforms/fftMul.cpp new file mode 100644 index 0000000..dc19412 --- /dev/null +++ b/math/transforms/fftMul.cpp @@ -0,0 +1,14 @@ +vector<cplx> mul(vector<cplx>& a, vector<cplx>& b) { + vector<cplx> c(a.size()), d(a.size()); + for (int i = 0; i < b.size(); i++) { + c[i] = {real(a[i]), real(b[i])}; + } + c = fft(c); + for (int i = 0; i < b.size(); i++) { + int j = (a.size() - i) % a.size(); + cplx x = (c[i] + conj(c[j])) / cplx{2, 0}; //fft(a)[i]; + cplx y = (c[i] - conj(c[j])) / cplx{0, 2}; //fft(b)[i]; + d[i] = x * y; + } + return fft(d, true); +} diff --git a/math/transforms/fftPerm.cpp b/math/transforms/fftPerm.cpp new file mode 100644 index 0000000..2b6fb10 --- /dev/null +++ b/math/transforms/fftPerm.cpp @@ -0,0 +1,8 @@ +int perm[MAXN]; //perm[i] = j in Zeile 10 +void genPerm(int n){ + ull mask = ~0ull << (__lg(n) - 1); + for (int i = 0, j = 0; i < n; i++) { + perm[i] = j; //if (i < j) swap(a[i], a[j]); + ull y = mask >> __builtin_ctz(~i); + j ^= y & (n - 1); +}} diff --git a/math/transforms/ntt.cpp b/math/transforms/ntt.cpp new file mode 100644 index 0000000..e1e4588 --- /dev/null +++ b/math/transforms/ntt.cpp @@ -0,0 +1,28 @@ +constexpr ll mod = 998244353; +constexpr ll root = 3; + +void fft(vector<ll>& a, bool inverse = 0) { + int n = sz(a); + for (int i = 0, j = 1; j < n - 1; ++j) { + for (int k = n >> 1; k > (i ^= k); k >>= 1); + if (j < i) swap(a[i], a[j]); + } + for (int s = 1; s < n; s *= 2) { + ll ws = powMod(root, (mod - 1) / s >> 1, mod); + if (inverse) ws = powMod(ws, mod - 2, mod); + for (int j = 0; j < n; j+= 2 * s) { + ll w = 1; + for (int k = 0; k < s; k++) { + ll u = a[j + k], t = a[j + s + k] * w; + t %= mod; + a[j + k] = (u + t) % mod; + a[j + s + k] = (u - t + mod) % mod; + w *= ws; + w %= mod; + }}} + if (inverse) { + ll div = powMod(n, mod - 2, mod); + for (ll i = 0; i < n; i++) { + a[i] *= div; + a[i] %= mod; +}}} diff --git a/math/transforms/orTransform.cpp b/math/transforms/orTransform.cpp new file mode 100644 index 0000000..fdb5bb8 --- /dev/null +++ b/math/transforms/orTransform.cpp @@ -0,0 +1,17 @@ +void fft(vector<ll>& a, bool inverse = 0) { + int n = sz(a); + for (int i = 0, j = 1; j < n - 1; ++j) { + for (int k = n >> 1; k > (i ^= k); k >>= 1); + if (j < i) swap(a[i], a[j]); + } + for (int s = 1; s < n; s *= 2) { + for (int j = 0; j < n; j+= 2 * s) { + for (int k = 0; k < s; k++) { + ll u = a[j + k], t = a[j + s + k]; + if (!inverse) { + a[j + k] = u + t; + a[j + s + k] = u; + } else { + a[j + k] = t; + a[j + s + k] = u - t; +}}}}} diff --git a/math/transforms/xorTransform.cpp b/math/transforms/xorTransform.cpp new file mode 100644 index 0000000..48e4df2 --- /dev/null +++ b/math/transforms/xorTransform.cpp @@ -0,0 +1,17 @@ +void fft(vector<ll>& a, bool inverse = 0) { + int n = sz(a); + for (int i = 0, j = 1; j < n - 1; ++j) { + for (int k = n >> 1; k > (i ^= k); k >>= 1); + if (j < i) swap(a[i], a[j]); + } + for (int s = 1; s < n; s *= 2) { + for (int j = 0; j < n; j+= 2 * s) { + for (int k = 0; k < s; k++) { + ll u = a[j + k], t = a[j + s + k]; + a[j + k] = u + t; + a[j + s + k] = u - t; + }}} + if (inverse) { + for (ll i = 0; i < n; i++) { + a[i] /= n; +}}} diff --git a/other/bitOps.cpp b/other/bitOps.cpp index ecb94fa..9666187 100644 --- a/other/bitOps.cpp +++ b/other/bitOps.cpp @@ -1,22 +1,18 @@ -// Bit an Position j auslesen. -(a & (1 << j)) != 0 -// Bit an Position j setzen. -a |= (1 << j) -// Bit an Position j löschen. -a &= ~(1 << j) -// Bit an Position j umkehren. -a ^= (1 << j) -// Wert des niedrigsten gesetzten Bits. -(a & -a) -// Setzt alle Bits auf 1. -a = -1 -// Setzt die ersten n Bits auf 1. Achtung: Overflows. -a = (1 << n) - 1 -// Iteriert über alle Teilmengen einer Bitmaske (außer der leeren Menge). -for (int subset = bitmask; subset > 0; subset = (subset - 1) & bitmask) -// Anzahl der gesetzten Bits. -int __builtin_popcount(unsigned int x); -int __builtin_popcountll(unsigned long long x); -// Anzahl der führenden 0-Bits. -int __builtin_clz(unsigned int x); -int __builtin_clzll(unsigned long long x); +// Iteriert über alle Teilmengen einer Bitmaske +// (außer der leeren Menge). +for (int subset = bitmask; subset > 0; + subset = (subset - 1) & bitmask) + +// Zählt Anzahl der gesetzten Bits. +int numberOfSetBits(int i) { + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; +} + +// Nächste Permutation in Bitmaske +// (z.B. 00111 => 01011 => 01101 => ...) +ll nextPerm(ll v) { + ll t = v | (v - 1); + return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctzll(v) + 1)); +} diff --git a/other/compiletime.cpp b/other/compiletime.cpp new file mode 100644 index 0000000..7734806 --- /dev/null +++ b/other/compiletime.cpp @@ -0,0 +1,7 @@ +template<int N> +struct Table { + int data[N]; + constexpr Table() : data {} { + for (int i = 0; i < N; i++) data[i] = i; +}}; +constexpr Table<100000> precalculated;
\ No newline at end of file diff --git a/other/divideAndConquer.cpp b/other/divideAndConquer.cpp new file mode 100644 index 0000000..92ec0ef --- /dev/null +++ b/other/divideAndConquer.cpp @@ -0,0 +1,27 @@ +vector<vector<ll>> dp; +vector<vector<ll>> C; + +void rec(int i, int j0, int j1, int k0, int k1) { + if (j1 < j0) return; + int jmid = (j0 + j1) / 2; + + dp[i][jmid] = inf; + int bestk = k0; + for (int k = k0; k < min(jmid, k1 + 1); ++k) { + if (dp[i - 1][k] + C[k + 1][jmid] < dp[i][jmid]) { + dp[i][jmid] = dp[i - 1][k] + C[k + 1][jmid]; + bestk = k; + }} + + rec(i, j0, jmid - 1, k0, bestk); + rec(i, jmid + 1, j1, bestk, k1); +} + +ll calc(int n, int k) { + dp = vector<vector<ll>>(k, vector<ll>(n, inf)); + for (int i = 0; i < n; i++) dp[0][i] = C[0][i]; + for (int i = 1; i < k; i++) { + rec(i, 0, n - 1, 0, n - 1); + } + return dp[k - 1][n - 1]; +} diff --git a/other/fastIO.cpp b/other/fastIO.cpp index 0077ce2..63f9ede 100644 --- a/other/fastIO.cpp +++ b/other/fastIO.cpp @@ -1,24 +1,24 @@ -void fastscan(int* number) { - bool negative = false; - register int c; - *number = 0; - c = getchar(); - while(c != '-' && (c < '0' || c > '9')) c = getchar(); - if (c == '-') negative = true, c = getchar(); - for (; c > 47 && c < 58; c = getchar()) *number = *number * 10 + c - 48; - if (negative) *number *= -1; +void fastscan(int& number) { + bool negative = false; + register int c; + number = 0; + c = getchar(); + while(c != '-' && (c < '0' || c > '9')) c = getchar(); + if (c == '-') negative = true, c = getchar(); + for (; c >= '0' && c <= '9'; c = getchar()) number = number * 10 + c - '0'; + if (negative) number *= -1; } void printPositive(int n) { - if (n == 0) return; - print(n / 10); - putchar(n % 10 + '0'); + if (n == 0) return; + printPositive(n / 10); + putchar(n % 10 + '0'); } void fastprint(int n) { - if(n == 0) { putchar('0'); return; } - if (n < 0) { - putchar('-'); - print(-n); - } else print(n); + if(n == 0) {putchar('0'); return;} + if (n < 0) { + putchar('-'); + printPositive(-n); + } else printPositive(n); } diff --git a/other/josephus2.cpp b/other/josephus2.cpp index a973609..5086e13 100644 --- a/other/josephus2.cpp +++ b/other/josephus2.cpp @@ -1,8 +1,8 @@ int rotateLeft(int n) { // Der letzte Überlebende, 1-basiert. - for (int i = 31; i >= 0; i--) + for (int i = 31; i >= 0; i--) { if (n & (1 << i)) { n &= ~(1 << i); break; - } + }} n <<= 1; n++; return n; } diff --git a/other/josephusK.cpp b/other/josephusK.cpp index 522b584..8d4df4d 100644 --- a/other/josephusK.cpp +++ b/other/josephusK.cpp @@ -1,4 +1,5 @@ -int josephus(int n, int k) { // Der letzte Überlebende, 0-basiert. +// Der letzte Überlebende, 0-basiert. +int josephus(int n, int k) { if (n == 1) return 0; return (josephus(n - 1, k) + k) % n; }
\ No newline at end of file diff --git a/other/knuth.cpp b/other/knuth.cpp new file mode 100644 index 0000000..f47dbe0 --- /dev/null +++ b/other/knuth.cpp @@ -0,0 +1,15 @@ +ll calc(int n, int k, const vector<vector<ll>> &C) { + vector<vector<ll>> dp(k, vector<ll>(n, inf)); + vector<vector<int>> opt(k, vector<int>(n + 1, n - 1)); + + for (int i = 0; i < n; i++) dp[0][i] = C[0][i]; + for (int i = 1; i < k; i++) { + for (int j = n - 1; j >= 0; --j) { + opt[i][j] = i == 1 ? 0 : opt[i - 1][j]; + for (int k = opt[i][j]; k <= min(opt[i][j + 1], j - 1); ++k) { + if (dp[i][j] <= dp[i - 1][k] + C[k + 1][j]) continue; + dp[i][j] = dp[i - 1][k] + C[k + 1][j]; + opt[i][j] = k; + }}} + return dp[k - 1][n - 1]; +} diff --git a/other/other.tex b/other/other.tex index d9ac362..3a70a36 100644 --- a/other/other.tex +++ b/other/other.tex @@ -1,73 +1,137 @@ \section{Sonstiges} -\subsection{Zeileneingabe} -\lstinputlisting{other/split.cpp} - -\subsection{Bit Operations} -\lstinputlisting{other/bitOps.cpp} - -% \subsection{Fast IO} -% \lstinputlisting{other/fastIO.cpp} - -\subsection{Rekursiver Abstieg und Abstrakter Syntaxbaum} -\lstinputlisting{other/parser.cpp} - -\subsection{Sonstiges} -\begin{lstlisting} -// Alles-Header. -#include <bits/stdc++.h> -// Schnelle Ein-/Ausgabe mit cin/cout. -ios::sync_with_stdio(false); -cin.tie(NULL); -// Set mit eigener Sortierfunktion. -set<point2, decltype(comp)> set1(comp); -// PI -#define PI (2*acos(0)) -// STL-Debugging, Compiler flags. --D_GLIBCXX_DEBUG -// 128-Bit Integer/Float. Zum Einlesen/Ausgeben in long long casten. -__int128, __float128 -\end{lstlisting} - -\subsection{Josephus-Problem} -$n$ Personen im Kreis, jeder $k$-te wird erschossen. -\begin{description} - \item[Spezialfall $k=2$:] Betrachte Binärdarstellung von $n$. - Für $n = 1b_1b_2b_3..b_n$ ist $b_1b_2b_3..b_n1$ die Position des letzten Überlebenden. - (Rotiere $n$ um eine Stelle nach links) - \lstinputlisting{other/josephus2.cpp} - \item[Allgemein:] Sei $F(n,k)$ die Position des letzten Überlebenden. - Nummeriere die Personen mit $0, 1, \ldots, n-1$. - Nach Erschießen der $k$-ten Person, hat der Kreis noch Größe $n-1$ und die Position des Überlebenden ist jetzt $F(n-1,k)$. - Also: $F(n,k) = (F(n-1,k)+k)\%n$. Basisfall: $F(1,k) = 0$. +\begin{algorithm}[optional]{Zeileneingabe} + \sourcecode{other/split.cpp} +\end{algorithm} + +\begin{algorithm}[optional]{Fast IO} + \sourcecode{other/fastIO.cpp} +\end{algorithm} + +\begin{algorithm}{Pragmas} + \sourcecode{other/pragmas.cpp} +\end{algorithm} + +\begin{algorithm}{Compiletime} + \begin{itemize} + \item überprüfen ob Compilezeit Berechnungen erlaubt sind! + \item braucht \code{c++14} oder höher! + \end{itemize} + \sourcecode{other/compiletime.cpp} +\end{algorithm} + +\begin{algorithm}{Timed} + Kann benutzt werdem un randomisierte Algorithmen so lange wie möglich laufen zu lassen. + \sourcecode{other/timed.cpp} +\end{algorithm} + +\begin{algorithm}{Bit Operations} + \begin{expandtable} + \begin{tabularx}{\linewidth}{|Ll|} + \hline + Bit an Position j lesen & \code{(x & (1 << j)) != 0} \\ + Bit an Position j setzten & \code{x |= (1 << j)} \\ + Bit an Position j löschen & \code{x &= ~(1 << j)} \\ + Bit an Position j flippen & \code{x ^= (1 << j)} \\ + Anzahl an führenden nullen ($x \neq 0$) & \code{__builtin_clzll(x)} \\ + Anzahl an schließenden nullen ($x \neq 0$) & \code{__builtin_ctzll(x)} \\ + Anzahl an bits & \code{__builtin_popcountll(x)} \\ + \hline + \end{tabularx}\\ + \end{expandtable} + \sourcecode{other/bitOps.cpp} +\end{algorithm} + +\begin{algorithm}{Overflow-sichere arithmetische Operationen} + Gibt zurück, ob es einen Overflow gab. Wenn nicht, enthält \code{c} das Ergebnis. + \begin{expandtable} + \begin{tabularx}{\linewidth}{|lR|} + \hline + Addition & \code{__builtin_saddll_overflow(a, b, &c)} \\ + Subtraktion & \code{__builtin_ssubll_overflow(a, b, &c)} \\ + Multiplikation & \code{__builtin_smulll_overflow(a, b, &c)} \\ + \hline + \end{tabularx} + \end{expandtable} +\end{algorithm} + +\begin{algorithm}{Sonstiges} + \sourcecode{other/stuff.cpp} +\end{algorithm} + +\begin{algorithm}{DP Optimizations} + Aufgabe: Partitioniere Array in genau $k$ zusammenhängende Teile mit minimalen Kosten: + $dp[i][j] = \min_{k<j}\{dp[i-1][k]+C[k][j]\}$. Es sei $A[i][j]$ das \emph{minimale} optimale + $k$ bei der Berechnung von $dp[i][j]$. + + \paragraph{Divide and Conquer} + Vorbedingung: $A[i][j - 1] \leq A[i][j]$. + + \method{calc}{berechnet das DP}{k\*n\*\log(n)} + \sourcecode{other/divideAndConquer.cpp} + + \paragraph{\textsc{Knuth}-Optimization} Vorbedingung: $A[i - 1][j] \leq A[i][j] \leq A[i][j + 1]$ + + \method{calc}{berechnet das DP}{n^2} + \sourcecode{other/knuth.cpp} + + \paragraph{Quadrangle inequality} Die Bedingung $\forall a\leq b\leq c\leq d: + C[a][d] + C[b][c] \geq C[a][c] + C[b][d]$ ist hinreichend für beide Optimierungen. +\end{algorithm} + +\begin{algorithm}{Parallel Binary Search} + \sourcecode{other/pbs.cpp} +\end{algorithm} + +\begin{algorithm}{Josephus-Problem} + $n$ Personen im Kreis, jeder $k$-te wird erschossen. + \begin{description} + \item[Spezialfall $\boldsymbol{k=2}$:] Betrachte $n$ Binär. + Für $n = 1b_1b_2b_3..b_n$ ist $b_1b_2b_3..b_n1$ die Position des letzten Überlebenden. + (Rotiere $n$ um eine Stelle nach links) + \end{description} + \sourcecode{other/josephus2.cpp} + + \begin{description} + \item[Allgemein:] Sei $F(n,k)$ die Position des letzten Überlebenden. + Nummeriere die Personen mit $0, 1, \ldots, n-1$. + Nach Erschießen der $k$-ten Person, hat der Kreis noch Größe $n-1$ und die Position des Überlebenden ist jetzt $F(n-1,k)$. + Also: $F(n,k) = (F(n-1,k)+k)\%n$. Basisfall: $F(1,k) = 0$. + \end{description} \lstinputlisting{other/josephusK.cpp} -\end{description} -\textbf{Beachte bei der Ausgabe, dass die Personen im ersten Fall von $1, \ldots, n$ nummeriert sind, im zweiten Fall von $0, \ldots, n-1$!} + \textbf{Beachte bei der Ausgabe, dass die Personen im ersten Fall von $\boldsymbol{1, \ldots, n}$ nummeriert sind, im zweiten Fall von $\boldsymbol{0, \ldots, n-1}$!} +\end{algorithm} \subsection{Gemischtes} \begin{itemize} + \item \textbf{(Minimum) Flow mit Demand \textit{d}:} + Erstelle neue Quelle $s'$ und Senke $t'$ und setzte die folgenden Kapazitäten: + \begin{align*} + c'(s',v)&=\sum_{u\in{}V}d(u,v)&c'(v,t')&=\sum_{u\in{}V}d(v,u)\\[-0.5ex] + c'(u,v)&=c(u,v)-d(u,v)&c'(t,s)&=x + \end{align*} + Löse Fluss auf $G'$ mit \textsc{Dinic's Algorithmus}, wenn alle Kanten von $s'$ saturiert sind ist der Fluss in $G$ gültig. $x$ beschränkt den Fluss in $G$ (Binary-Search für minflow, $\infty$ sonst). \item \textbf{\textsc{Johnsons} Reweighting Algorithmus:} - Füge neue Quelle \lstinline{S} hinzu, mit Kanten mit Gewicht 0 zu allen Knoten. - Nutze \textsc{Bellmann-Ford} zum Betsimmen der Entfernungen \lstinline{d[i]} von \lstinline{S} zu allen anderen Knoten. - Stoppe, wenn es negative Zyklen gibt. - Sonst ändere die gewichte von allen Kanten \lstinline{(u,v)} im ursprünglichen Graphen zu \lstinline{d[u]+w[u,v]-d[v]}. + Initialisiere alle Entfernungen mit \texttt{d[i] = 0}. Berechne mit \textsc{Bellmann-Ford} kürzeste Entfernungen. + Falls es einen negativen Zyklus gibt abrrechen. + Sonst ändere die Gewichte von allen Kanten \texttt{(u,v)} im ursprünglichen Graphen zu \texttt{d[u]+w[u,v]-d[v]}. Dann sind alle Kantengewichte nichtnegativ, \textsc{Dijkstra} kann angewendet werden. \item \textbf{System von Differenzbeschränkungen:} Ändere alle Bedingungen in die Form $a-b \leq c$. - Für jede Bedingung füge eine Kante \lstinline{(b,a)} mit Gweicht \lstinline{c} ein. - Füge Quelle \lstinline{s} hinzu, mit Kanten zu allen Knoten mit Gewicht 0. - Nutze \textsc{Bellmann-Ford}, um die kürzesten Pfade von \lstinline{s} aus zu finden. - \lstinline{d[v]} ist mögliche Lösung für \lstinline{v}. - - \item \textbf{Min-Weight-Vertex-Cover im bipartiten Graph:} - Partitioniere in \lstinline{A, B} und füge Kanten \lstinline{s -> A} mit Gewicht \lstinline{w(A)} und Kanten \lstinline{B -> t} mit Gewicht \lstinline{w(B)} hinzu. - Füge Kanten mit Kapazität $\infty$ von \lstinline{A} nach \lstinline{B} hinzu, wo im originalen Graphen Kanten waren. + Für jede Bedingung füge eine Kante \texttt{(b,a)} mit Gweicht \texttt{c} ein. + Füge Quelle \texttt{s} hinzu, mit Kanten zu allen Knoten mit Gewicht 0. + Nutze \textsc{Bellmann-Ford}, um die kürzesten Pfade von \texttt{s} aus zu finden. + \texttt{d[v]} ist mögliche Lösung für \texttt{v}. + + \item \textbf{Min-Weight-Vertex-Cover im Bipartiten Graph:} + Partitioniere in \texttt{A, B} und füge Kanten \texttt{s}\,$\rightarrow$\,\texttt{A} mit Gewicht \texttt{w(A)} und Kanten \texttt{B}\,$\rightarrow$\,\texttt{t} mit Gewicht \texttt{w(B)} hinzu. + Füge Kanten mit Kapazität $\infty$ von \texttt{A} nach \texttt{B} hinzu, wo im originalen Graphen Kanten waren. Max-Flow ist die Lösung.\newline Im Residualgraphen: - \begin{itemize}[nosep] + \begin{itemize} \item Das Vertex-Cover sind die Knoten inzident zu den Brücken. \emph{oder} - \item Die Knoten in \lstinline{A}, die \emph{nicht} von \lstinline{s} erreichber sind und die Knoten in \lstinline{B}, die von \lstinline{erreichber} sind. + \item Die Knoten in \texttt{A}, die \emph{nicht} von \texttt{s} erreichbar sind und die Knoten in \texttt{B}, die von \texttt{erreichbar} sind. \end{itemize} \item \textbf{Allgemeiner Graph:} @@ -75,11 +139,12 @@ $n$ Personen im Kreis, jeder $k$-te wird erschossen. $\Rightarrow$ Max Weight Independent Set ist Komplement von Min Weight Vertex Cover. \item \textbf{Bipartiter Graph:} - Min Vertex Cover (kleinste Menge Kanten, die alle Knoten berühren) = Max Matching. + Min Vertex Cover (kleinste Menge Knoten, die alle Kanten berühren) = Max Matching. + Richte Kanten im Matching von $B$ nach $A$ und sonst von $A$ nach $B$, makiere alle Knoten die von einem ungematchten Knoten in $A$ erreichbar sind, das Vertex Cover sind die makierten Knoten aus $B$ und die unmakierten Knoten aus $A$. \item \textbf{Bipartites Matching mit Gewichten auf linken Knoten:} Minimiere Matchinggewicht. - Lösung: Sortiere Knoten links aufsteigend nach Gewicht, danach nutze normlen Algorithmus (\textsc{Kuhn}, Seite \pageref{kuhn}) + Lösung: Sortiere Knoten links aufsteigend nach Gewicht, danach nutze normalen Algorithmus (\textsc{Kuhn}, Seite \pageref{kuhn}) \item \textbf{Satz von \textsc{Pick}:} Sei $A$ der Flächeninhalt eines einfachen Gitterpolygons, $I$ die Anzahl der Gitterpunkte im Inneren und $R$ die Anzahl der Gitterpunkte auf dem Rand. @@ -118,7 +183,7 @@ $n$ Personen im Kreis, jeder $k$-te wird erschossen. \newline Entferne letzte Zeile und Spalte und berechne Betrag der Determinante. - \item \textbf{\textsc{Dilworth}'s-Theorem:} + \item \textbf{\textsc{Dilworths}-Theorem:} Sei $S$ eine Menge und $\leq$ eine partielle Ordnung ($S$ ist ein Poset). Eine \emph{Kette} ist eine Teilmenge $\{x_1,\ldots,x_n\}$ mit $x_1 \leq \ldots \leq x_n$. Eine \emph{Partition} ist eine Menge von Ketten, sodass jedes $s \in S$ in genau einer Kette ist. @@ -130,62 +195,65 @@ $n$ Personen im Kreis, jeder $k$-te wird erschossen. Berechnung: Maximales Matching in bipartitem Graphen. Dupliziere jedes $s \in S$ in $u_s$ und $v_s$. Falls $x \leq y$, füge Kante $u_x \to v_y$ hinzu. - Wenn Matching zu langsam ist, versuche Struktur des Posets auszunutzen und evtl. anders eine maximale Anitkette zu finden. - + Wenn Matching zu langsam ist, versuche Struktur des Posets auszunutzen und evtl. anders eine maximale Anitkette zu finden. + + \item \textbf{\textsc{Turan}'s-Theorem:} + Die Anzahl an Kanten in einem Graphen mit $n$ Knoten der keine clique der größe $x+1$ enthält ist: + \begin{align*} + ext(n, K_{x+1}) &= \binom{n}{2} - \left[\left(x - (n \bmod x)\right) \cdot \binom{\floor{\frac{n}{x}}}{2} + \left(n\bmod x\right) \cdot \binom{\ceil{\frac{n}{x}}}{2}\right] + \end{align*} + + \item \textbf{\textsc{Euler}'s-Polyedersatz:} + In planaren Graphen gilt $n-m+f-c=1$. + + \item \textbf{\textsc{Pythagoreische Tripel}:} + Sei $m>n>0,~k>0$ und $m\not\equiv n \bmod 2$ dann beschreibt diese Formel alle Pythagoreischen Tripel eindeutig: + \[k~\cdot~\Big(~a=m^2-n^2,\quad b=2mn,\quad c=m^2+n^2~\Big)\] + \item \textbf{\textsc{Mo}'s Algorithm:} SQRT-Decomposition auf $n$ Intervall Queries $[l,r]$. Gruppiere Queries in $\sqrt{n}$ Blöcke nach linker Grenze $l$. Sortiere nach Block und bei gleichem Block nach rechter Grenze $r$. Beantworte Queries offline durch schrittweise Vergrößern/Verkleinern des aktuellen Intervalls. - Laufzeit:~$\mathcal{O}(n\cdot\sqrt{n})$. + Laufzeit:~\runtime{n\cdot\sqrt{n}}. (Anzahl der Blöcke als Konstante in Code schreiben.) - + \item \textbf{Centroids of a Tree:} - Ein \emph{Centroid} ist ein Konten, der einen Baum in Komponenten der maximalen Größe $\frac{\vert V \vert}{2}$ splitted. + Ein \emph{Centroid} ist ein Knoten, der einen Baum in Komponenten der maximalen Größe $\frac{\abs{V}}{2}$ splitted. Es kann $2$ Centroids geben! - \textbf{Centroid Decomposition:} + + \item \textbf{Centroid Decomposition:} Wähle zufälligen Knoten und mache DFS. - Verschiebe ausgewählten Knoten in Richtung des tiefsten Teilbaums, bis Centroid gefunden. Entferne Knoten, mache rekursiv in Teilbäumen weiter. Laufzeit:~$\mathcal{O}(\vert V \vert \log(\vert V \vert))$. + Verschiebe ausgewählten Knoten in Richtung des tiefsten Teilbaums, bis Centroid gefunden. Entferne Knoten, mache rekursiv in Teilbäumen weiter. Laufzeit:~\runtime{\abs{V} \cdot \log(\abs{V})}. + \item \textbf{Gregorian Calendar:} Der Anfangstag des Jahres verhält sich periodisch alle $400$ Jahre. - \item \textbf{Kreuzprodukt} - \[ - a \times b = - \begin{pmatrix} - a_1 \\ - a_2 \\ - a_3 - \end{pmatrix} - \times - \begin{pmatrix} - b_1 \\ - b_2 \\ - b_3 - \end{pmatrix} - = - \begin{pmatrix} - a_2b_3 - a_3b_2 \\ - a_3b_1 - a_1b_3 \\ - a_1b_2 - a_2b_1 - \end{pmatrix} - \] + \item \textbf{Pivotsuche und Rekursion auf linkem und rechtem Teilarray:} + Suche gleichzeitig von links und rechts nach Pivot, um Worst Case von + $\runtime{n^2}$ zu $\runtime{n\log n}$ zu verbessern. \end{itemize} \subsection{Tipps \& Tricks} \begin{itemize} - \item Run Tim Error: + \item Run Time Error: \begin{itemize} + \item Stack Overflow? Evtl. rekursive Tiefensuche auf langem Pfad? \item Array-Grenzen überprüfen. Indizierung bei $0$ oder bei $1$ beginnen? \item Abbruchbedingung bei Rekursion? \item Evtl. Memory Limit Exceeded? - \item $n$ und $m$ verwechselt? \end{itemize} - + + \item Strings: + \begin{itemize} + \item Soll \lstinline{"aa"} kleiner als \lstinline{"z"} sein oder nicht? + \item bit \code{0x20} beeinflusst Groß-/Kleinschreibung. + \end{itemize} + \item Gleitkommazahlen: \begin{itemize} - \item \lstinline{NaN}? Evtl. ungültige Werte für mathematische Funktionen, z.B. \lstinline{acos(1.00000000000001)}? - \item Flasches Runden bei negativen Zahlen? Abschneiden $\neq$ Abrunden! - \item Output in wissenschaftlicher Notation (\lstinline{1e-25})? + \item \lstinline{NaN}? Evtl. ungültige Werte für mathematische Funktionen, z.B. \mbox{\lstinline{acos(1.00000000000001)}}? + \item Falsches Runden bei negativen Zahlen? Abschneiden $\neq$ Abrunden! + \item genügend Präzision oder Output in wissenschaftlicher Notation (\lstinline{1e-25})? \item Kann \lstinline{-0.000} ausgegeben werden? \end{itemize} @@ -194,11 +262,13 @@ $n$ Personen im Kreis, jeder $k$-te wird erschossen. \item Lies Aufgabe erneut. Sorgfältig! \item Mehrere Testfälle in einer Datei? Probiere gleichen Testcase mehrfach hintereinander. \item Integer Overflow? Teste maximale Eingabegrößen und mache Überschlagsrechnung. - \item Einabegrößen überprüfen. Sonderfälle ausprobieren. + \item Integer Division rundet zur $0$ $\neq$ abrunden. + \item Eingabegrößen überprüfen. Sonderfälle ausprobieren. \begin{itemize} - \item $n = 0$, $n = -1$, $n = 1$, $n = 2^{31}-1$, $n = -2^{31} = 2147483648$ + \item $n = 0$, $n = -1$, $n = 1$, $n = 2^{31}-1$, $n = -2^{31}$ \item $n$ gerade/ungerade \item Graph ist leer/enthält nur einen Knoten. + \item Liste ist leer/enthält nur ein Element. \item Graph ist Multigraph (enthält Schleifen/Mehrfachkanten). \item Sind Kanten gerichtet/ungerichtet? \item Polygon ist konkav/selbstschneidend. diff --git a/other/parser.cpp b/other/parser.cpp deleted file mode 100644 index 94b7829..0000000 --- a/other/parser.cpp +++ /dev/null @@ -1,61 +0,0 @@ -struct Token { // In globalem Vektor, Zugriff über globale Variable. - int type; // Definiere Konstanten. - double value; - Token(int type) : type(type) {} - Token(int type, int value) : type(type), value(value) {} -}; - -struct Expression { // Die folgenden Klassen nur für den AST. - virtual ~Expression() {}; - virtual double getValue() = 0; -}; - -struct Atom : public Expression { - double value; - Atom(int value) : value(value) {}; - double getValue() { return value; } -}; - -struct BinaryExpression : public Expression { - Expression *lhs, *rhs; - BinaryExpression(Expression *lhs, Expression *rhs) : lhs(lhs), rhs(rhs) {} - ~BinaryExpression() { delete lhs; delete rhs; } -}; - -struct Addition : public BinaryExpression { - Addition(Expression *lhs, Expression *rhs) : BinaryExpression(lhs, rhs) {} - double getValue() { return lhs->getValue() + rhs->getValue(); } -}; - -Expression* parseF() { - Expression *lhs; - switch(tokens[token].type) { - case NUMBER: return new Atom(tokens[token++].value); - case LEFT_PAR: - token++; - lhs = parseA(); - token++; - return lhs; - default: - return NULL; -}} - -Expression* parseA_(Expression *lhs) { - Expression *plus, *minus; - if (token >= (int)tokens.size()) return lhs; - switch(tokens[token].type) { - case ADDITION: - token++; - plus = new Addition(lhs, parseS()); - return parseA_(plus); - case SUBTRACTION: - token++; - minus = new Subtraction(lhs, parseS()); - return parseA_(minus); - default: - return lhs; -}} - -Expression* parseA() { - Expression *lhs = parseS(); return parseA_(lhs); -} diff --git a/other/pbs.cpp b/other/pbs.cpp new file mode 100644 index 0000000..45577e2 --- /dev/null +++ b/other/pbs.cpp @@ -0,0 +1,32 @@ +// Q = Anzahl der Anfragen +// C = Anzahl der Schritte der Operation + +vector<vector<int>> focus; +vector<int> low, high, ans; + +ans.assign(Q, C + 1); +low.assign(Q, 0); +high.assign(Q, C); +focus.assign(C + 1, vector<int>()); +for (bool changed = true; changed;) { + changed = false; + for (int i = 0; i <= C; i++) focus[i].clear(); + for (int i = 0; i < Q; i++) { + if (low[i] > high[i]) continue; + focus[(low[i] + high[i]) / 2].pb(i); + } + + // Simulation zurücksetzen + + for (int k = 0; k <= C; k++) { + // Simulationsschritt + + for (int q : focus[k]) { + changed = true; + if (/* Eigenschaft schon erfüllt */) { + // Antwort updaten + high[q] = k - 1; + ans[q] = min(ans[q], k); + } else { + low[q] = k + 1; +}}}} diff --git a/other/pragmas.cpp b/other/pragmas.cpp new file mode 100644 index 0000000..f8cf060 --- /dev/null +++ b/other/pragmas.cpp @@ -0,0 +1,6 @@ +#pragma GCC optimize("Ofast") +#pragma GCC optimize ("unroll-loops") +#pragma GCC target("sse,sse2,sse3,ssse3,sse4," + "popcnt,abm,mmx,avx,tune=native") +#pragma GCC target("fpmath=sse,sse2") // no excess precision +#pragma GCC target("fpmath=387") // force excess precision
\ No newline at end of file diff --git a/other/stuff.cpp b/other/stuff.cpp new file mode 100644 index 0000000..81286d8 --- /dev/null +++ b/other/stuff.cpp @@ -0,0 +1,30 @@ +// Alles-Header. +#include <bits/stdc++.h> + +// Setzt das deutsche Tastaturlayout. +setxkbmap de + +// Schnelle Ein-/Ausgabe mit cin/cout. +ios::sync_with_stdio(false); +cin.tie(nullptr); + +// Set mit eigener Sortierfunktion. +// Typ muss nicht explizit angegeben werden. +set<point2, decltype(comp)> set1(comp); + +// STL-Debugging, Compiler flags. +-D_GLIBCXX_DEBUG +#define _GLIBCXX_DEBUG + +// 128-Bit Integer/Float. Muss zum Einlesen/Ausgeben +// in einen int oder long long gecastet werden. +__int128, __float128 + +// float mit Decimaldarstellung +#include <decimal/decimal> +std::decimal::decimal128 + +// 1e18 < INF < Max_Value / 2 +constexpr long long INF = 0x3FFF_FFFF_FFFF_FFFFll; +// 1e9 < INF < Max_Value / 2 +constexpr int INF = 0x3FFF_FFFF; diff --git a/other/timed.cpp b/other/timed.cpp new file mode 100644 index 0000000..20eec70 --- /dev/null +++ b/other/timed.cpp @@ -0,0 +1,3 @@ +int times = clock(); +//run for 900ms +while (clock()-times < 900) {...} diff --git a/string/ahoCorasick.cpp b/string/ahoCorasick.cpp index d221fcb..530490e 100644 --- a/string/ahoCorasick.cpp +++ b/string/ahoCorasick.cpp @@ -1,57 +1,54 @@ -// Laufzeit: O(n + m + z), n = #Text, m = Summe #Pattern, z = #Matches -// Findet mehrere Patterns gleichzeitig in einem String. -// 1) Wurzel erstellen: aho.push_back(vertex()); -// 2) Mit addString(0, pattern, idx); Patterns hinzufügen. -// 3) finishAutomaton(0) aufrufen. -// 4) Mit state = go(state, c) in nächsten Zustand wechseln. -// DANACH: Wenn patterns-Vektor nicht leer ist: Hier enden alle -// enthaltenen Patterns. -// ACHTUNG: Die Zahlenwerte der auftretenden Buchstaben müssen -// zusammenhängend sein und bei 0 beginnen! -struct vertex { - int next[ALPHABET_SIZE], failure; - int character; - vector<int> patterns; // Indizes der Patterns, die hier enden. - vertex() { for (int i = 0; i < ALPHABET_SIZE; i++) next[i] = -1; } -}; -vector<vertex> aho; +constexpr ll ALPHABET_SIZE = 26; +constexpr char OFFSET = 26; +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; -void addString(int v, vector<int> &pattern, int patternIdx) { - for (int i = 0; i < (int)pattern.size(); i++) { - if (aho[v].next[pattern[i]] == -1) { - aho[v].next[pattern[i]] = aho.size(); - aho.push_back(vertex()); - aho.back().character = pattern[i]; - } - v = aho[v].next[pattern[i]]; - } - aho[v].patterns.push_back(patternIdx); -} + AhoCorasick() {aho.push_back(vert(-1, 0));} -void finishAutomaton(int v) { - for (int i = 0; i < ALPHABET_SIZE; i++) - if (aho[v].next[i] == -1) aho[v].next[i] = v; + // Call once for each pattern. + void addString(string &s, int patternIdx) { + int v = 0; + for (char c : s) { + int idx = c - OFFSET; + if (aho[v].nxt[idx] == -1) { + aho[v].nxt[idx] = sz(aho); + aho.emplace_back(idx, v); + } + v = aho[v].nxt[idx]; + } + aho[v].patterns.push_back(patternIdx); + } - queue<int> q; - for (int i = 0; i < ALPHABET_SIZE; i++) { - if (aho[v].next[i] != v) { - aho[aho[v].next[i]].failure = v; - q.push(aho[v].next[i]); - }} - while (!q.empty()) { - int r = q.front(); q.pop(); - for (int i = 0; i < ALPHABET_SIZE; i++) { - if (aho[r].next[i] != -1) { - q.push(aho[r].next[i]); - int f = aho[r].failure; - while (aho[f].next[i] == -1) f = aho[f].failure; - aho[aho[r].next[i]].failure = aho[f].next[i]; - for (int j = 0; j < (int)aho[aho[f].next[i]].patterns.size(); j++) { - aho[aho[r].next[i]].patterns.push_back( - aho[aho[f].next[i]].patterns[j]); -}}}}} + 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); + } + return aho[v].suffix; + } -int go(int v, int c) { - if (aho[v].next[c] != -1) return aho[v].next[c]; - else return go(aho[v].failure, c); -} + 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]; + else return v == 0 ? 0 : go(getSuffix(v), idx); + } +};
\ No newline at end of file diff --git a/string/deBruijn.cpp b/string/deBruijn.cpp new file mode 100644 index 0000000..e829137 --- /dev/null +++ b/string/deBruijn.cpp @@ -0,0 +1,7 @@ +string deBruijn(int n, char mi = '0', char ma = '1') { + string res, c(1, mi); + do { + if (n % sz(c) == 0) res += c; + } while(next(c, n, mi, ma)); + return res; +} diff --git a/string/duval.cpp b/string/duval.cpp new file mode 100644 index 0000000..6d80e95 --- /dev/null +++ b/string/duval.cpp @@ -0,0 +1,21 @@ +vector<pair<int, int>> duval(const string& s) { + vector<pair<int, int>> res; + for (int i = 0; i < sz(s);) { + int j = i + 1, k = i; + for (; j < sz(s) && s[k] <= s[j]; j++) { + if (s[k] < s[j]) k = i; + else k++; + } + while (i <= k) { + res.push_back({i, i + j - k}); + i += j - k; + }} + return res; +} + +int minrotation(const string& s) { + auto parts = duval(s+s); + for (auto e : parts) { + if (e.first < sz(s) && e.second >= sz(s)) { + return e.first; +}}} diff --git a/string/kmp.cpp b/string/kmp.cpp index 450b368..282019e 100644 --- a/string/kmp.cpp +++ b/string/kmp.cpp @@ -1,25 +1,23 @@ -// Laufzeit: O(n + m), n = #Text, m = #Pattern -vector<int> kmpPreprocessing(string &sub) { - vector<int> b(sub.length() + 1); - b[0] = -1; - int i = 0, j = -1; - while (i < (int)sub.length()) { - while (j >= 0 && sub[i] != sub[j]) j = b[j]; - i++; j++; - b[i] = j; - } - return b; +vector<int> kmpPreprocessing(const string& sub) { + vector<int> b(sub.size() + 1); + b[0] = -1; + int i = 0, j = -1; + while (i < (int)sub.size()) { + while (j >= 0 && sub[i] != sub[j]) j = b[j]; + i++; j++; + b[i] = j; + } + return b; } - -vector<int> kmpSearch(string &s, string &sub) { - vector<int> pre = kmpPreprocessing(sub), result; - int i = 0, j = 0; - while (i < (int)s.length()) { - while (j >= 0 && s[i] != sub[j]) j = pre[j]; - i++; j++; - if (j == (int)sub.length()) { - result.push_back(i - j); - j = pre[j]; - }} - return result; +vector<int> kmpSearch(const string& s, const string& sub) { + vector<int> pre = kmpPreprocessing(sub), result; + int i = 0, j = 0; + while (i < (int)s.size()) { + while (j >= 0 && s[i] != sub[j]) j = pre[j]; + i++; j++; + if (j == (int)sub.size()) { + result.push_back(i - j); + j = pre[j]; + }} + return result; } diff --git a/string/longestCommonSubsequence.cpp b/string/longestCommonSubsequence.cpp index 7bcf851..dd2368e 100644 --- a/string/longestCommonSubsequence.cpp +++ b/string/longestCommonSubsequence.cpp @@ -1,14 +1,12 @@ -// Laufzeit: O(|a|*|b|) -string lcss(string &a, string &b) { - int m[a.length() + 1][b.length() + 1], x=0, y=0; - memset(m, 0, sizeof(m)); - for(int y = a.length() - 1; y >= 0; y--) { - for(int x = b.length() - 1; x >= 0; x--) { +string lcss(string& a, string& b) { + vector<vector<int>> m(a.size() + 1, vector<int>(b.size() + 1)); + for(int y = a.size() - 1; y >= 0; y--) { + for(int x = b.size() - 1; x >= 0; x--) { if(a[y] == b[x]) m[y][x] = 1 + m[y+1][x+1]; else m[y][x] = max(m[y+1][x], m[y][x+1]); }} // Für die Länge: return m[0][0]; - string res; - while(x < b.length() && y < a.length()) { + string res; int x=0; int y=0; + while(x < b.size() && y < a.size()) { if(a[y] == b[x]) res += a[y++], x++; else if(m[y][x+1] > m[y+1][x+1]) x++; else y++; diff --git a/string/lyndon.cpp b/string/lyndon.cpp new file mode 100644 index 0000000..6a131a5 --- /dev/null +++ b/string/lyndon.cpp @@ -0,0 +1,11 @@ +bool next(string& s, int n, char mi = '0', char ma = '1') { + for (ll i = sz(s), j = sz(s); i < n; i++) + s.push_back(s[i % j]); + while(!s.empty() && s.back() == ma) s.pop_back(); + if (s.empty()) { + s = mi; + return false; + } else { + s.back()++; + return true; +}} diff --git a/string/manacher.cpp b/string/manacher.cpp index 8bd58fb..a1cf2da 100644 --- a/string/manacher.cpp +++ b/string/manacher.cpp @@ -1,30 +1,27 @@ -char input[MAX_N]; -char s[2 * MAX_N + 1]; -int longest[2 * MAX_N + 1]; +string a, b; //a needs to be set +vector<int> longest; -void setDots() { - s[0] = '.'; - int j = 1; - for (int i = 0; i < (int)strlen(input); i++) { - s[j++] = input[i]; - s[j++] = '.'; - } - s[j] = '\0'; -} +//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 = strlen(s); - memset(longest, 0, sizeof(longest)); - - 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 && - s[i + longest[i] + 1] == s[i - longest[i] - 1]) longest[i]++; - if (i + longest[i] > last) { - center = i; - last = i + longest[i]; - } - } - for (int i = 0; i < n; i++) longest[i] = 2 * longest[i] + 1; -} + int center = 0, last = 0, n = sz(b); + 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 (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; +}
\ No newline at end of file diff --git a/string/rollingHash.cpp b/string/rollingHash.cpp index 0df4c87..2185207 100644 --- a/string/rollingHash.cpp +++ b/string/rollingHash.cpp @@ -1,19 +1,17 @@ -ll q = 31; // Größer als Alphabetgröße. q=31,53,311 +// q = 29, 53, 101, 257, 1009, 65537 +// or choose q random from [sigma, m) +// m = 1500000001, 1600000009, 1700000009 struct Hasher { - string s; - ll mod; - vector<ll> power, pref; - Hasher(const string& s, ll mod) : s(s), mod(mod) { - power.push_back(1); - for (int i = 1; i < (int)s.size(); i++) - power.push_back(power.back() * q % mod); - pref.push_back(0); - for (int i = 0; i < (int)s.size(); i++) - pref.push_back((pref.back() * q % mod + s[i]) % mod); - } + vector<ll> power = {1}, pref = {0}; + ll m, q; char c; + Hasher(const string& s, ll m, ll q, char c) : + m(m), q(q), c(c) { + for (char x : s) { + power.push_back(power.back() * q % m); + pref.push_back((pref.back() * q % m + (x - c)) % m); + }} - // Berechnet hash(s[l..r]). l,r inklusive. - ll hash(int l, int r) { - return (pref[r+1] - power[r-l+1] * pref[l] % mod + mod) % mod; - } -}; + ll hash(int l, int r) { // Berechnet hash(s[l..r)). + return (pref[r] - power[r-l] * pref[l] % m + m) % m; + } +};
\ No newline at end of file diff --git a/string/string.tex b/string/string.tex index e13b516..f426c61 100644 --- a/string/string.tex +++ b/string/string.tex @@ -1,48 +1,118 @@ \section{Strings} -\subsection{\textsc{Knuth-Morris-Pratt}-Algorithmus} -\lstinputlisting{string/kmp.cpp} +\begin{algorithm}{\textsc{Knuth-Morris-Pratt}-Algorithmus} + \begin{methods} + \method{kmpSearch}{sucht \code{sub} in \code{s}}{\abs{s}+\abs{sub}} + \end{methods} + \sourcecode{string/kmp.cpp} +\end{algorithm} -\subsection{\textsc{Aho-Corasick}-Automat} -\lstinputlisting{string/ahoCorasick.cpp} +\begin{algorithm}{Z-Algorithmus} + \begin{methods}[ll] + $z_i\coloneqq$ Längstes gemeinsames Präfix von $s_0\cdots s_{n-1}$ und $s_i\cdots s_{n-1}$ & \runtime{n} + \end{methods} + Suchen: Z-Algorithmus auf \code{P\$S} ausführen, Positionen mit $z_i=\abs{P}$ zurückgeben + \sourcecode{string/z.cpp} +\end{algorithm} -\subsection{Trie} -\lstinputlisting{string/trie.cpp} +\begin{algorithm}{Trie} + \sourcecode{string/trie.cpp} +\end{algorithm} -\subsection{Suffix-Baum} -\lstinputlisting{string/suffixTree.cpp} +\begin{algorithm}{Longest Common Subsequence} + \begin{methods} + \method{lcss}{findet längste gemeinsame Sequenz}{\abs{a}\*\abs{b}} + \end{methods} + \sourcecode{string/longestCommonSubsequence.cpp} +\end{algorithm} -\subsection{Suffix-Array} -\lstinputlisting{string/suffixArray.cpp} +\begin{algorithm}{\textsc{Manacher}'s Algorithm, Longest Palindrome} + \begin{methods} + \method{init}{transformiert \code{string a}}{n} + \method{manacher}{berechnet Länge der Palindrome}{n} + \end{methods} + \sourcecode{string/manacher.cpp} +\end{algorithm} -\subsection{Suffix-Automaton} -\lstinputlisting{string/suffixAutomaton.cpp} -\begin{itemize}[nosep] - \item \textbf{Ist \lstinline{w} Substring von \lstinline{s}?} - Baue Automaten für \lstinline{s} und wende ihn auf \lstinline{w} an. - Wenn alle Übergänge vorhanden sind, ist \lstinline{w} Substring von \lstinline{s}. +\begin{algorithm}{Rolling Hash} + \sourcecode{string/rollingHash.cpp} +\end{algorithm} - \item \textbf{Ist \lstinline{w} Suffix von \lstinline{s}?} - Wie oben. - Überprüfe am Ende, ob aktueller Zustand ein Terminal ist. +\begin{algorithm}{\textsc{Aho-Corasick}-Automat} + \begin{methods}[ll] + sucht patterns im Text & \runtime{\abs{Text}+\sum\abs{pattern}+\abs{matches}} + \end{methods} + \begin{enumerate} + \item mit \code{addString(pattern, idx);} Patterns hinzufügen. + \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 + \end{enumerate} + \sourcecode{string/ahoCorasick.cpp} +\end{algorithm} - \item \textbf{Anzahl verschiedener Substrings.} - Jeder Pfad im Automaten entspricht einem Substring. - Für einen Knoten ist die Anzahl der ausgehenden Pfade gleich der Summe über die Anzahlen der Kindknoten plus 1. - Der letzte Summand ist der Pfad, der in diesem Knoten endet. +\begin{algorithm}{Suffix-Baum} + \begin{methods} + \method{SuffixTree}{berechnet einen Suffixbaum}{\abs{s}} + \method{extend}{fügt den nächsten Buchstaben aus \code{s} ein}{1} + \end{methods} + \sourcecode{string/suffixTree.cpp} +\end{algorithm} - \item \textbf{Wie oft taucht \lstinline{w} in \lstinline{s} auf?} - Sei \lstinline{p} der Zustand nach Abarbeitung von \lstinline{w}. - Lösung ist Anzahl der Pfade, die in \lstinline{p} starten und in einem Terminal enden. - Diese Zahl lässt sich wie oben rekursiv berechnen. - Bei jedem Knoten darf nur dann plus 1 gerechnet werden, wenn es ein Terminal ist. -\end{itemize} +\begin{algorithm}{Suffix-Array} + \begin{methods} + \method{SuffixArray}{berechnet ein Suffix Array}{\abs{s}\*\log^2(\abs{s})} + \method{lcp}{berechnet den logest common prefix}{\log(\abs{s})} + \method{}{von \code{s[x]} und \code{s[y]}}{} + \end{methods} + \textbf{ACHTUNG:} \code{s} muss mit einem sentinel enden! \code{'\$'} oder \code{'#'} + \sourcecode{string/suffixArray.cpp} +\end{algorithm} -\subsection{Longest Common Subsequence} -\lstinputlisting{string/longestCommonSubsequence.cpp} +\begin{algorithm}{Suffix-Automaton} + \sourcecode{string/suffixAutomaton.cpp} + \begin{itemize} + \item \textbf{Ist \textit{w} Substring von \textit{s}?} + Baue Automaten für \textit{s} und wende ihn auf \textit{w} an. + Wenn alle Übergänge vorhanden sind, ist \textit{w} Substring von \textit{s}. + + \item \textbf{Ist \textit{w} Suffix von \textit{s}?} + Wie oben. + Überprüfe am Ende, ob aktueller Zustand ein Terminal ist. + + \item \textbf{Anzahl verschiedener Substrings.} + Jeder Pfad im Automaten entspricht einem Substring. + Für einen Knoten ist die Anzahl der ausgehenden Pfade gleich der Summe über die Anzahlen der Kindknoten plus 1. + Der letzte Summand ist der Pfad, der in diesem Knoten endet. + + \item \textbf{Wie oft taucht \textit{w} in \textit{s} auf?} + Sei \textit{p} der Zustand nach Abarbeitung von \textit{w}. + Lösung ist Anzahl der Pfade, die in \textit{p} starten und in einem Terminal enden. + Diese Zahl lässt sich wie oben rekursiv berechnen. + Bei jedem Knoten darf nur dann plus 1 gerechnet werden, wenn es ein Terminal ist. + \end{itemize} +\end{algorithm} -\subsection{Rolling Hash} -\lstinputlisting{string/rollingHash.cpp} - -\subsection{\textsc{Manacher}'s Algorithm, Longest Palindrome} -\lstinputlisting{string/manacher.cpp} +\begin{algorithm}{Lyndon und De-Bruijn} + \begin{itemize} + \item \textbf{Lyndon-Wort:} Ein Wort das lexikographisch kleiner ist als jede seiner Rotationen. + \item Jedes Wort kann \emph{eindeutig} in eine nicht ansteigende Folge von Lyndon-Worten zerlegt werden. + \item Für Lyndon-Worte $u, v$ mit $u<v$ gilt, dass $uv$ auch ein Lyndon-Wort ist. + \end{itemize} + \begin{methods} + \method[, Durchschnitt $\Theta(1)$]{next}{lexikographisch nächstes Lyndon-Wort}{n} + \method{duval}{zerlegt $s$ in Lyndon-Worte}{n} + \method{minrotation}{berechnet kleinste Rotation von $s$}{n} + \end{methods} + \sourcecode{string/lyndon.cpp} + \sourcecode{string/duval.cpp} + \begin{itemize} + \item \textbf{De-Bruijn-Sequenze $\boldsymbol{B(\Sigma, n)}$:}~ein Wort das jedes Wort der Länge $n$ genau einmal als substring enthält (und minimal ist). Wobei $B(\Sigma, n)$ zyklisch betrachtet wird. + \item es gibt $\frac{(k!)^{k^{n-1}}}{k^{n}}$ verschiedene $B(\Sigma, n)$ + \item $B(\Sigma, n)$ hat Länge $\abs{\Sigma}^n$ + \end{itemize} + \begin{methods} + \method{deBruijn}{berechnet ein festes $B(\Sigma, n)$}{\abs{\Sigma}^n} + \end{methods} + \sourcecode{string/deBruijn.cpp} +\end{algorithm} diff --git a/string/suffixArray.cpp b/string/suffixArray.cpp index 17a10cf..66b0ee9 100644 --- a/string/suffixArray.cpp +++ b/string/suffixArray.cpp @@ -1,31 +1,37 @@ -struct SuffixArray { // MAX_LG = ceil(log2(MAX_N)) - static const int MAX_N = 100010, MAX_LG = 17; - pair<pair<int, int>, int> L[MAX_N]; - int P[MAX_LG + 1][MAX_N], n, step, count; - int suffixArray[MAX_N], lcpArray[MAX_N]; +struct SuffixArray { + vector<pair<pair<int, int>, int>> L; + int n, step, count; + vector<vector<int>> P; + vector<int> SA, LCP; - SuffixArray(const string &s) : n(s.size()) { // Laufzeit: O(n*log^2(n)) + SuffixArray(const string& s) : n(sz(s)) { + SA.resize(n); LCP.resize(n); L.resize(n); + P.assign(ceil(log2(n)) + 1, vector<int>(n)); for (int i = 0; i < n; i++) P[0][i] = s[i]; - suffixArray[0] = 0; // Falls n == 1. - for (step = 1, count = 1; count < n; step++, count <<= 1) { - for (int i = 0; i < n; i++) L[i] = - {{P[step-1][i], i+count < n ? P[step-1][i+count] : -1}, i}; - sort(L, L + n); - for (int i = 0; i < n; i++) P[step][L[i].second] = i > 0 && - L[i].first == L[i-1].first ? P[step][L[i-1].second] : i; - } - for (int i = 0; i < n; i++) suffixArray[i] = L[i].second; - for (int i = 1; i < n; i++) - lcpArray[i] = lcp(suffixArray[i - 1], suffixArray[i]); + for (step = 1, count = 1; count < n; step++, count *= 2) { + for (int i = 0; i < n; i++) + L[i] = {{P[step-1][i], + i+count < n ? P[step-1][i+count] : -1}, i}; + sort(L.begin(), L.end()); + for (int i = 0; i < n; i++) { + P[step][L[i].second] = + i > 0 && L[i].first == L[i-1].first ? + P[step][L[i-1].second] : i; + }} + for (int i = 0; i < n; i++) SA[i] = L[i].second; + for (int i = 1; i < n; i++) LCP[i] = lcp(SA[i - 1], SA[i]); } - // x und y sind Indizes im String, nicht im Suffixarray. - int lcp(int x, int y) { // Laufzeit: O(log(n)) - int k, ret = 0; + // x and y are text-indices, not SA-indices. + int lcp(int x, int y) { + int ret = 0; if (x == y) return n - x; - for (k = step - 1; k >= 0 && x < n && y < n; k--) - if (P[k][x] == P[k][y]) - x += 1 << k, y += 1 << k, ret += 1 << k; + for (int k = step - 1; k >= 0 && x < n && y < n; k--) + if (P[k][x] == P[k][y]) { + x += 1 << k; + y += 1 << k; + ret += 1 << k; + } return ret; } }; diff --git a/string/suffixAutomaton.cpp b/string/suffixAutomaton.cpp index 7f4885c..15265c8 100644 --- a/string/suffixAutomaton.cpp +++ b/string/suffixAutomaton.cpp @@ -1,42 +1,42 @@ -#define ALPHABET_SIZE 26 +constexpr char MIN_CHAR = 'a'; +constexpr long long ALPHABET_SIZE = 26; struct SuffixAutomaton { struct State { - int length; int link; int next[ALPHABET_SIZE]; - State() { memset(next, 0, sizeof(next)); } + int length, link; + vector<int> next; + State() : next(ALPHABET_SIZE) {} }; - static const int MAX_N = 100000; // Maximale Länge des Strings. - State states[2 * MAX_N]; + vector<State> states; int size, last; - SuffixAutomaton(string &s) { // Laufzeit: O(|s|) + SuffixAutomaton(string &s) { + states.resize(2 * sz(s)); size = 1; last = 0; states[0].length = 0; states[0].link = -1; - for (auto c : s) extend(c); + for (auto c : s) extend(c - MIN_CHAR); } - void extend(char c) { - c -= 'a'; // Werte von c müssen bei 0 beginnen. + void extend(int c) { int current = size++; states[current].length = states[last].length + 1; int pos = last; - while (pos != -1 && !states[pos].next[(int)c]) { - states[pos].next[(int)c] = current; + while (pos != -1 && !states[pos].next[c]) { + states[pos].next[c] = current; pos = states[pos].link; } if (pos == -1) states[current].link = 0; else { - int q = states[pos].next[(int)c]; + int q = states[pos].next[c]; if (states[pos].length + 1 == states[q].length) { states[current].link = q; } else { int clone = size++; states[clone].length = states[pos].length + 1; states[clone].link = states[q].link; - memcpy(states[clone].next, states[q].next, - sizeof(states[q].next)); - while (pos != -1 && states[pos].next[(int)c] == q) { - states[pos].next[(int)c] = clone; + states[clone].next = states[q].next; + while (pos != -1 && states[pos].next[c] == q) { + states[pos].next[c] = clone; pos = states[pos].link; } states[q].link = states[current].link = clone; @@ -44,22 +44,23 @@ struct SuffixAutomaton { last = current; } - // Paar mit Startposition und Länge des LCS. Index in Parameter s. - ii longestCommonSubstring(string &s) { // Laufzeit: O(|s|) + // Pair with start index and length of LCS. + // Index in parameter t. + pair<int, int> longestCommonSubstring(string &t) { int v = 0, l = 0, best = 0, bestpos = 0; - for (int i = 0; i < (int)s.size(); i++) { - int c = s[i] - 'a'; + for (int i = 0; i < (int)t.size(); i++) { + int c = t[i] - MIN_CHAR; while (v && !states[v].next[c]) { v = states[v].link; l = states[v].length; } - if (states[v].next[c]) { v = states[v].next[c]; l++; } - if (l > best) { best = l; bestpos = i; } + if (states[v].next[c]) {v = states[v].next[c]; l++;} + if (l > best) {best = l; bestpos = i;} } - return ii(bestpos - best + 1, best); + return {bestpos - best + 1, best}; } - // Berechnet die Terminale des Automaten. + // Returns all terminals of the automaton. vector<int> calculateTerminals() { vector<int> terminals; int pos = last; diff --git a/string/suffixTree.cpp b/string/suffixTree.cpp index fe065b2..f96992e 100644 --- a/string/suffixTree.cpp +++ b/string/suffixTree.cpp @@ -1,78 +1,83 @@ -// Baut Suffixbaum online auf. Laufzeit: O(n) -// Einmal initSuffixTree() aufrufen und dann extend für jeden Buchstaben. -// '\0'-Zeichen (oder ähnliches) an den Text anhängen! -string s; -int root, lastIdx, needsSuffix, pos, remainder, curVert, curEdge, curLen; -struct Vert { - int start, end, suffix; // Kante [start,end) - map<char, int> next; - int len() { return min(end, pos + 1) - start; } -}; -vector<Vert> tree; +struct SuffixTree { + struct Vert { + int start, end, suffix; + map<char, int> next; + }; + string s; + int root, lastIdx, needsSuffix, pos, remainder; + int curVert, curEdge, curLen; + // Each Vertex gives its children range as [start, end) + vector<Vert> tree; -int newVert(int start, int end) { - Vert v; - v.start = start; - v.end = end; - v.suffix = 0; - tree.push_back(v); - return ++lastIdx; -} + SuffixTree(string& s) : s(s) { + needsSuffix = remainder = curEdge = curLen = 0; + lastIdx = pos = -1; + root = curVert = newVert(-1, -1); + for (int i = 0; i < sz(s); i++) extend(); + } -void addSuffixLink(int vert) { - if (needsSuffix) tree[needsSuffix].suffix = vert; - needsSuffix = vert; -} + int newVert(int start, int end) { + Vert v; + v.start = start; + v.end = end; + v.suffix = 0; + tree.push_back(v); + return ++lastIdx; + } -bool fullImplicitEdge(int vert) { - if (curLen >= tree[vert].len()) { - curEdge += tree[vert].len(); - curLen -= tree[vert].len(); - curVert = vert; - return true; - } - return false; -} + int len(Vert& v) { + return min(v.end, pos + 1) - v.start; + } -void initSuffixTree() { - needsSuffix = remainder = curEdge = curLen = 0; - lastIdx = pos = -1; - root = curVert = newVert(-1, -1); -} + void addSuffixLink(int vert) { + if (needsSuffix) tree[needsSuffix].suffix = vert; + needsSuffix = vert; + } -void extend() { - pos++; - needsSuffix = 0; - remainder++; - while (remainder) { - if (curLen == 0) curEdge = pos; - if (!tree[curVert].next.count(s[curEdge])) { - int leaf = newVert(pos, s.size()); - tree[curVert].next[s[curEdge]] = leaf; - tree[curVert].next[s[curEdge]] = leaf; - addSuffixLink(curVert); - } else { - int nxt = tree[curVert].next[s[curEdge]]; - if (fullImplicitEdge(nxt)) continue; - if (s[tree[nxt].start + curLen] == s[pos]) { - curLen++; - addSuffixLink(curVert); - break; - } - int split = newVert(tree[nxt].start, tree[nxt].start + curLen); - tree[curVert].next[s[curEdge]] = split; - int leaf = newVert(pos, s.size()); - tree[split].next[s[pos]] = leaf; - tree[nxt].start += curLen; - tree[split].next[s[tree[nxt].start]] = nxt; - addSuffixLink(split); - } - remainder--; - if (curVert == root && curLen) { - curLen--; - curEdge = pos - remainder + 1; - } else { - curVert = tree[curVert].suffix ? tree[curVert].suffix : root; - } - } -} + bool fullImplicitEdge(int vert) { + if (curLen >= len(tree[vert])) { + curEdge += len(tree[vert]); + curLen -= len(tree[vert]); + curVert = vert; + return true; + } + return false; + } + + void extend() { + pos++; + needsSuffix = 0; + remainder++; + while (remainder) { + if (curLen == 0) curEdge = pos; + if (!tree[curVert].next.count(s[curEdge])) { + int leaf = newVert(pos, sz(s)); + tree[curVert].next[s[curEdge]] = leaf; + tree[curVert].next[s[curEdge]] = leaf; + addSuffixLink(curVert); + } else { + int nxt = tree[curVert].next[s[curEdge]]; + if (fullImplicitEdge(nxt)) continue; + if (s[tree[nxt].start + curLen] == s[pos]) { + curLen++; + addSuffixLink(curVert); + break; + } + int split = newVert(tree[nxt].start, + tree[nxt].start + curLen); + tree[curVert].next[s[curEdge]] = split; + int leaf = newVert(pos, sz(s)); + tree[split].next[s[pos]] = leaf; + tree[nxt].start += curLen; + tree[split].next[s[tree[nxt].start]] = nxt; + addSuffixLink(split); + } + remainder--; + if (curVert == root && curLen) { + curLen--; + curEdge = pos - remainder + 1; + } else { + curVert = tree[curVert].suffix ? tree[curVert].suffix + : root; + }}} +};
\ No newline at end of file diff --git a/string/trie.cpp b/string/trie.cpp index fa9ec49..f112e1e 100644 --- a/string/trie.cpp +++ b/string/trie.cpp @@ -1,25 +1,33 @@ // Zahlenwerte müssen bei 0 beginnen und zusammenhängend sein. +constexpr int ALPHABET_SIZE = 2; struct node { - int children[ALPHABET_SIZE], c; // c = #Wörter, die hier enden. - node () { - idx = -1; - for (int i = 0; i < ALPHABET_SIZE; i++) children[i] = -1; - } + int words, wordEnds; vector<int> children; + node() : words(0), wordEnds(0), children(ALPHABET_SIZE, -1){} }; -vector<node> trie; // Anlegen mit trie.push_back(node()); +vector<node> trie = {node()}; -void insert(int vert, vector<int> &txt, int s) { // Laufzeit: O(|txt|) - if (s == (int)txt.size()) { trie[vert].c++; return; } - if (trie[vert].children[txt[s]] == -1) { - trie[vert].children[txt[s]] = trie.size(); - trie.push_back(node()); - } - insert(trie[vert].children[txt[s]], txt, s + 1); +int insert(vector<int>& word) { + int id = 0; + for (int c : word) { + trie[id].words++; + if (trie[id].children[c] < 0) { + trie[id].children[c] = trie.size(); + trie.emplace_back(); + } + id = trie[id].children[c]; + } + trie[id].words++; + trie[id].wordEnds++; + return id; } -int contains(int vert, vector<int> &txt, int s) { // Laufzeit: O(|txt|) - if (s == (int)txt.size()) return trie[vert].c; - if (trie[vert].children[txt[s]] != -1) { - return contains(trie[vert].children[txt[s]], txt, s + 1); - } else return 0; +void erase(vector<int>& word) { + int id = 0; + for (int c : word) { + trie[id].words--; + id = trie[id].children[c]; + if (id < 0) return; + } + trie[id].words--; + trie[id].wordEnds--; } diff --git a/string/z.cpp b/string/z.cpp new file mode 100644 index 0000000..dbd5ce5 --- /dev/null +++ b/string/z.cpp @@ -0,0 +1,15 @@ +vector<int> Z(const vector<int> &s) { + int n = sz(s); + vector<int> z(n, n); + int l = 0, r = 0, p, q; + for (int i = 1; i < n; ++i) { + if (i <= r && z[i - l] < r - i + 1) { + z[i] = z[i - l]; + } else { + if (i > r) p = 0, q = i; + else p = r - i + 1, q = r + 1; + while (q < n && s[p] == s[q]) ++p, ++q; + z[i] = q - i, l = i, r = q - 1; + }} + return z; +} Binary files differ@@ -1,40 +1,65 @@ -\documentclass[a4paper,9pt]{scrartcl} + +%maybe size 9pt if too many pages +\documentclass[a4paper,fontsize=7.8pt]{scrartcl} % General information. -\newcommand{\teamname}{Hello KITty} +\newcommand{\teamname}{Let's party!} \newcommand{\university}{Karlsruhe Institute of Technology} +% Options +\newif\ifoptional +%\optionaltrue + % Font encoding. \usepackage[T1]{fontenc} \usepackage[ngerman]{babel} \usepackage[utf8]{inputenc} +\usepackage[hidelinks,pdfencoding=auto]{hyperref} -% Include headers. -\input{latexHeaders/layout} -\input{latexHeaders/math} +% Include headers. +\usepackage{latexHeaders/layout} +\usepackage{latexHeaders/math} +%\usepackage{latexHeaders/listings} \input{latexHeaders/listings} +\usepackage{latexHeaders/commands} % Title and author information. \title{Team Contest Reference} \author{\teamname \\ \university} -\date{26. November 2017} +\date{\today} \begin{document} % Titlepage with table of contents. -\maketitle \setlength{\columnsep}{1cm} -% \begin{multicols}{2} -% \tableofcontents -% \end{multicols} -% \newpage +\optional{ +\maketitle +\begin{multicols*}{3} + \tableofcontents +\end{multicols*} +} +\newpage % Content. -\begin{multicols*}{2} +\begin{multicols*}{3} \input{datastructures/datastructures} \input{graph/graph} \input{geometry/geometry} \input{math/math} +\end{multicols*} + \clearpage + \input{math/tables} +\begin{multicols*}{3} \input{string/string} + \input{java/java} \input{other/other} + \input{template/template} + \clearpage + \ifodd\value{page} + \else + \null + \thispagestyle{empty} + \clearpage + \fi + \input{tests/test} \end{multicols*} \end{document} diff --git a/template/console.cpp b/template/console.cpp new file mode 100644 index 0000000..fe5c489 --- /dev/null +++ b/template/console.cpp @@ -0,0 +1,2 @@ +alias comp="g++ -std=gnu++17 -O2 -Wall -Wextra -Wconversion -Wshadow" +alias dbg="comp -g -fsanitize=address -fsanitize=undefined" diff --git a/template/template.cpp b/template/template.cpp new file mode 100644 index 0000000..48c2b99 --- /dev/null +++ b/template/template.cpp @@ -0,0 +1,18 @@ +#include <bits/stdc++.h> +using namespace std; + +#define fora(i, n) for (int i = 0; i < n; ++i) +#define forb(i, n) for (int i = 1; i <= n; ++i) +#define sz(x) ((int)(x).size()) +#define all(x) (x).begin(), (x).end() +#define _ << " " << +#define debug(x) #x << " = " << (x) + +using ll = long long; +using ld = long double; +using pii = std::pair<int, int>; + +int main() { + std::ios::sync_with_stdio(false); + std::cin.tie(nullptr); +} diff --git a/template/template.tex b/template/template.tex new file mode 100644 index 0000000..3525ddf --- /dev/null +++ b/template/template.tex @@ -0,0 +1,9 @@ +\section{Template} + +\begin{algorithm}{C++} + \sourcecode{template/template.cpp} +\end{algorithm} + +\begin{algorithm}{Console} + \sourcecode{template/console.cpp} +\end{algorithm} diff --git a/tests/gcc5bug.cpp b/tests/gcc5bug.cpp new file mode 100644 index 0000000..55efa3b --- /dev/null +++ b/tests/gcc5bug.cpp @@ -0,0 +1,4 @@ +//https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68203 +struct A { + pair<int, int> values[1000000]; +};
\ No newline at end of file diff --git a/tests/precision.cpp b/tests/precision.cpp new file mode 100644 index 0000000..a1ab1f6 --- /dev/null +++ b/tests/precision.cpp @@ -0,0 +1,8 @@ +#include <cfloat> + +int main() { + cout << "Mode: " << FLT_EVAL_METHOD << endl; + double a = atof("1.2345678"); + double b = a*a; + cout << b - 1.52415765279683990130 << '\n'; +}
\ No newline at end of file diff --git a/tests/test.tex b/tests/test.tex new file mode 100644 index 0000000..95f1645 --- /dev/null +++ b/tests/test.tex @@ -0,0 +1,44 @@ +\section{Tests} +Dieser Abschnitt enthält lediglich Dinge die während der Practicesession getestet werden sollten! + +\subsection{GCC} +\begin{itemize} + \item sind c++14 Feature vorhanden? + \item sind c++17 Feature vorhanden? + \item kompiliert dieser Code: +\end{itemize} +\lstinputlisting{tests/gcc5bug.cpp} +\begin{itemize} + \item funktioniert \code{\_\_int128}? + \item funktionieren Pragmas? + \item funktionieren \code{constexpr} zur Compilezeit (+Zeitlimit)? + \item wie groß ist \code{sizeof(char*)}? + \item wie groß ist \code{RAND\_MAX}? + \item funktioniert \code{random_device}? (und gib es unerschiedliche Ergebnisse?) + \item funktioniert \code{clock()}? +\end{itemize} + +\subsection{Java} +\begin{itemize} + \item startet eclipse? + \item funktionieren Java8 feature (lambdas)? +\end{itemize} + +\subsection{Judge} +\begin{itemize} + \item ist der Checker casesensitive? + \item wie werden zusätzliches Whitespacecharacter bei sonst korrektem Output behandelt? + \item vergleiche ausführungszeit auf dem judge und lokal (z.b. mit Primzahl Sieb) +\end{itemize} +\lstinputlisting{tests/whitespace.cpp} + +\subsection{Precision} +\begin{itemize} + \item Mode $0$ means no excess precision + \item Mode $2$ means excess precision (all operations in $80$\,bit floats) +\end{itemize} +\begin{itemize} + \item Result $0$ without excess precision (expected floating point error) + \item \textasciitilde$8e^{-17}$ with excess precision (real value) +\end{itemize} +\lstinputlisting{tests/precision.cpp} diff --git a/tests/whitespace.cpp b/tests/whitespace.cpp new file mode 100644 index 0000000..71ad9a4 --- /dev/null +++ b/tests/whitespace.cpp @@ -0,0 +1 @@ +"\r\r\r\n\t \r\n\r"
\ No newline at end of file |
