summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormzuenni <michi.zuendorf@gmail.com>2022-06-27 17:19:28 +0200
committermzuenni <michi.zuendorf@gmail.com>2022-06-27 17:19:28 +0200
commit5ab8a5088b729a9953b8dff1b2a985dc8fb2098b (patch)
treeed40d6936c0e9eee40ba62751cbf99ecddbaddc2
parentadabbad9c51cf7cd3874bfde8eac1fbcf84fec10 (diff)
updated tcr
-rw-r--r--.gitignore222
-rw-r--r--Makefile7
-rw-r--r--README.md5
-rw-r--r--datastructures/LCT.cpp178
-rw-r--r--datastructures/RMQ.cpp27
-rw-r--r--datastructures/datastructures.tex144
-rw-r--r--datastructures/dynamicConvexHull.cpp75
-rw-r--r--datastructures/fenwickTree.cpp24
-rw-r--r--datastructures/fenwickTree2.cpp21
-rw-r--r--datastructures/fenwickTreeNiklas.cpp56
-rw-r--r--datastructures/firstUnused.cpp13
-rw-r--r--datastructures/lazyPropagation1.cpp47
-rw-r--r--datastructures/lazyPropagation2.cpp50
-rw-r--r--datastructures/monotonicConvexHull.cpp21
-rw-r--r--datastructures/persistent.cpp19
-rw-r--r--datastructures/persistentArray.cpp26
-rw-r--r--datastructures/segmentTree.cpp58
-rw-r--r--datastructures/segmentTree2D.cpp58
-rw-r--r--datastructures/sparseTable.cpp37
-rw-r--r--datastructures/stlHashMap.cpp17
-rw-r--r--datastructures/stlPQ.cpp3
-rw-r--r--datastructures/stlRope.cpp2
-rw-r--r--datastructures/stlTree.cpp14
-rw-r--r--datastructures/treap.cpp98
-rw-r--r--datastructures/unionFind.cpp25
-rw-r--r--datastructures/unionFind2.cpp27
-rw-r--r--datastructures/waveletTree.cpp48
-rw-r--r--geometry/antipodalPoints.cpp13
-rw-r--r--geometry/circle.cpp33
-rw-r--r--geometry/closestPair.cpp39
-rw-r--r--geometry/convexHull.cpp39
-rw-r--r--geometry/formulars.cpp174
-rw-r--r--geometry/formulars3d.cpp53
-rw-r--r--geometry/geometry.tex50
-rw-r--r--geometry/lines.cpp29
-rw-r--r--geometry/linesAndSegments.cpp90
-rw-r--r--geometry/polygon.cpp33
-rw-r--r--geometry/segmentIntersection.cpp63
-rw-r--r--geometry/sortAround.cpp10
-rw-r--r--geometry/spheres.cpp29
-rw-r--r--geometry/triangle.cpp40
-rw-r--r--graph/2sat.cpp60
-rw-r--r--graph/LCA.cpp24
-rw-r--r--graph/LCA_sparse.cpp32
-rw-r--r--graph/TSP.cpp41
-rw-r--r--graph/articulationPoints.cpp66
-rw-r--r--graph/bellmannFord.cpp29
-rw-r--r--graph/bitonicTSP.cpp51
-rw-r--r--graph/bitonicTSPsimple.cpp28
-rw-r--r--graph/blossom.cpp84
-rw-r--r--graph/bronKerbosch.cpp24
-rw-r--r--graph/capacityScaling.cpp65
-rw-r--r--graph/centroid.cpp22
-rw-r--r--graph/connect.cpp31
-rw-r--r--graph/cycleCounting.cpp65
-rw-r--r--graph/dfs.tex16
-rw-r--r--graph/dijkstra.cpp34
-rw-r--r--graph/dinicScaling.cpp102
-rw-r--r--graph/euler.cpp54
-rw-r--r--graph/floydWarshall.cpp31
-rw-r--r--graph/graph.tex301
-rw-r--r--graph/havelHakimi.cpp19
-rw-r--r--graph/hld.cpp52
-rw-r--r--graph/hopcroftKarp.cpp71
-rw-r--r--graph/kruskal.cpp9
-rw-r--r--graph/lca.cpp28
-rw-r--r--graph/matching.cpp66
-rw-r--r--graph/maxCarBiMatch.cpp8
-rw-r--r--graph/maxWeightBipartiteMatching.cpp107
-rw-r--r--graph/minCostMaxFlow.cpp95
-rw-r--r--graph/pushRelabel.cpp31
-rw-r--r--graph/pushRelabel2.cpp109
-rw-r--r--graph/pushRelabel3.cpp66
-rw-r--r--graph/scc.cpp38
-rw-r--r--graph/stoerWagner.cpp53
-rw-r--r--graph/treeIsomorphism.cpp41
-rw-r--r--java/bigInteger.java25
-rw-r--r--java/inputA.java4
-rw-r--r--java/inputB.java7
-rw-r--r--java/java.tex37
-rw-r--r--java/output.java7
-rw-r--r--latexHeaders/commands.sty49
-rw-r--r--latexHeaders/layout.sty81
-rw-r--r--latexHeaders/layout.tex43
-rw-r--r--latexHeaders/listings.sty106
-rw-r--r--latexHeaders/listings.tex84
-rw-r--r--latexHeaders/math.sty98
-rw-r--r--latexHeaders/math.tex70
-rw-r--r--math/berlekampMassey.cpp30
-rw-r--r--math/bigint.cpp514
-rw-r--r--math/binomial.cpp18
-rw-r--r--math/binomial2.cpp32
-rw-r--r--math/binomial3.cpp9
-rw-r--r--math/chineseRemainder.cpp18
-rw-r--r--math/cycleDetection.cpp16
-rw-r--r--math/discreteLogarithm.cpp25
-rw-r--r--math/discreteNthRoot.cpp5
-rw-r--r--math/divisors.cpp11
-rw-r--r--math/extendedEuclid.cpp5
-rw-r--r--math/fft.cpp34
-rw-r--r--math/gauss.cpp12
-rw-r--r--math/gcd-lcm.cpp5
-rw-r--r--math/goldenSectionSearch.cpp14
-rw-r--r--math/inversions.cpp34
-rw-r--r--math/inversionsMerge.cpp27
-rw-r--r--math/kthperm.cpp14
-rw-r--r--math/legendre.cpp19
-rw-r--r--math/lgsFp.cpp13
-rw-r--r--math/linearCongruence.cpp5
-rw-r--r--math/longestIncreasingSubsequence.cpp44
-rw-r--r--math/math.tex890
-rw-r--r--math/matrixPower.cpp16
-rw-r--r--math/millerRabin.cpp20
-rw-r--r--math/mobius.cpp25
-rw-r--r--math/modExp.cpp9
-rw-r--r--math/modMulIterativ.cpp9
-rw-r--r--math/modPowIterativ.cpp16
-rw-r--r--math/modSqrt.cpp23
-rw-r--r--math/multInv.cpp4
-rw-r--r--math/permIndex.cpp14
-rw-r--r--math/phi.cpp33
-rw-r--r--math/piLegendre.cpp23
-rw-r--r--math/piLehmer.cpp52
-rw-r--r--math/polynomial.cpp65
-rw-r--r--math/primeSieve.cpp31
-rw-r--r--math/primes.cpp39
-rw-r--r--math/primitiveRoot.cpp40
-rw-r--r--math/rho.cpp22
-rw-r--r--math/simpson.cpp12
-rw-r--r--math/spheres.cpp24
-rw-r--r--math/squfof.cpp89
-rw-r--r--math/tables.tex18
-rw-r--r--math/tables/binom.tex28
-rw-r--r--math/tables/composite.tex26
-rw-r--r--math/tables/nim.tex96
-rw-r--r--math/tables/numbers.tex59
-rw-r--r--math/tables/platonic.tex39
-rw-r--r--math/tables/probability.tex27
-rw-r--r--math/tables/series.tex33
-rw-r--r--math/tables/stuff.tex32
-rw-r--r--math/tables/twelvefold.tex32
-rw-r--r--math/transforms/all.cpp62
-rw-r--r--math/transforms/andTransform.cpp17
-rw-r--r--math/transforms/fft.cpp20
-rw-r--r--math/transforms/fftMul.cpp14
-rw-r--r--math/transforms/fftPerm.cpp8
-rw-r--r--math/transforms/ntt.cpp28
-rw-r--r--math/transforms/orTransform.cpp17
-rw-r--r--math/transforms/xorTransform.cpp17
-rw-r--r--other/bitOps.cpp40
-rw-r--r--other/compiletime.cpp7
-rw-r--r--other/divideAndConquer.cpp27
-rw-r--r--other/fastIO.cpp34
-rw-r--r--other/josephus2.cpp4
-rw-r--r--other/josephusK.cpp3
-rw-r--r--other/knuth.cpp15
-rw-r--r--other/other.tex260
-rw-r--r--other/parser.cpp61
-rw-r--r--other/pbs.cpp32
-rw-r--r--other/pragmas.cpp6
-rw-r--r--other/stuff.cpp30
-rw-r--r--other/timed.cpp3
-rw-r--r--string/ahoCorasick.cpp103
-rw-r--r--string/deBruijn.cpp7
-rw-r--r--string/duval.cpp21
-rw-r--r--string/kmp.cpp44
-rw-r--r--string/longestCommonSubsequence.cpp14
-rw-r--r--string/lyndon.cpp11
-rw-r--r--string/manacher.cpp51
-rw-r--r--string/rollingHash.cpp32
-rw-r--r--string/string.tex142
-rw-r--r--string/suffixArray.cpp52
-rw-r--r--string/suffixAutomaton.cpp49
-rw-r--r--string/suffixTree.cpp151
-rw-r--r--string/trie.cpp44
-rw-r--r--string/z.cpp15
-rw-r--r--tcr.pdfbin326648 -> 641186 bytes
-rw-r--r--tcr.tex49
-rw-r--r--template/console.cpp2
-rw-r--r--template/template.cpp18
-rw-r--r--template/template.tex9
-rw-r--r--tests/gcc5bug.cpp4
-rw-r--r--tests/precision.cpp8
-rw-r--r--tests/test.tex44
-rw-r--r--tests/whitespace.cpp1
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
+
+*~
diff --git a/Makefile b/Makefile
index 010ba2f..0338a34 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 21b8775..7a518c5 100644
--- a/README.md
+++ b/README.md
@@ -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;
+}
diff --git a/tcr.pdf b/tcr.pdf
index eb97fc7..b3a9efa 100644
--- a/tcr.pdf
+++ b/tcr.pdf
Binary files differ
diff --git a/tcr.tex b/tcr.tex
index 675edf4..93a3fc9 100644
--- a/tcr.tex
+++ b/tcr.tex
@@ -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