├── .gitignore ├── Makefile ├── README.md ├── data-structures ├── link-cut-tree │ ├── main.cpp │ └── remark.tex ├── splay │ ├── main.cpp │ └── remark.tex ├── suffix-array │ ├── main.cpp │ └── remark.tex └── suffix-automaton │ ├── main.cpp │ └── remark.tex ├── graph ├── Hopcroft-Karp │ ├── main.cpp │ └── remark.tex ├── KM-algorithm │ ├── main.cpp │ └── remark.tex ├── Maximum-Flow │ ├── main.cpp │ └── remark.tex ├── Minimum-cost-Flow │ ├── main.cpp │ └── remark.tex ├── get-bridge │ ├── main.cpp │ └── remark.tex └── 最小树形图 │ ├── main.cpp │ └── remark.tex ├── math ├── geometry │ ├── main.cpp │ └── remark.tex ├── numbertheory │ ├── main.cpp │ └── remark.tex └── numerical │ ├── main.cpp │ └── remark.tex ├── other ├── Manacher │ ├── main.cpp │ └── remark.tex └── simplex │ ├── main.cpp │ └── remark.tex └── parse.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.aux 2 | *.log 3 | *.out 4 | *.pyg 5 | *.toc 6 | 7 | *.swp 8 | *.swo 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | main.pdf: main.tex 2 | xelatex main.tex 3 | xelatex main.tex #get content 4 | 5 | main.tex: parse.py 6 | python2 parse.py > main.tex 7 | 8 | .PHONY: clean 9 | clean: 10 | rm main.out main.aux main.log main.toc main.tex main.pdf -f 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 数据结构 2 | =========== 3 | 4 | * 后缀数组 5 | * 后缀自动机 6 | * splay 7 | * link-cut tree 8 | * 可持久化treap 9 | * AC自动机 10 | * 树链剖分 11 | * 树的点分治 12 | * 树的边分治 13 | 14 | 图论 15 | ========== 16 | 17 | * 图的基本结构 18 | * 强联通分量 19 | * 无向图求桥 20 | * 无向图求割点 21 | * 二分图匹配 22 | + 匈牙利算法 23 | + Hopcroft-Karp算法 24 | * 二分图最优匹配 25 | + KM 算法 26 | * 最小树形图 27 | + 朱刘算法 28 | * 最大密度子图 29 | + 01分数规划 && 网络流 30 | * 无向图全局最小割 31 | * 度数限制的最小生成树 32 | * 最小直径生成树 33 | * 最优比率生成树 34 | * 最小环 35 | * k短路 36 | 37 | 38 | 网络流 39 | ========== 40 | * 最大流 41 | + Dinic 42 | + SAP 43 | * 最小费用最大流(spfa增广,zkw费用流) 44 | + spfa 增广 45 | + zkw费用流 46 | * 上下界最大流 47 | * 上下界最小费用最大流 48 | * 无源无汇可行流 49 | * 无源无汇最小费用可行流 50 | 51 | 字符串 52 | ========= 53 | * KMP 54 | * 扩展KMP 55 | * Manacher回文子串 56 | * 字符串最小表示 57 | 58 | 其它 59 | ========= 60 | * 树的hash 61 | * 梭哈牌型的比较函数 62 | * 麻将 63 | * 最大团的搜索算法 64 | * FFT (非递归) 65 | * FFT 混合基 66 | * 表达式计算 67 | * 单纯形 68 | -------------------------------------------------------------------------------- /data-structures/link-cut-tree/main.cpp: -------------------------------------------------------------------------------- 1 | const int MAXN = 100010; 2 | struct Node { 3 | Node *ch[2], *p; int size, value; 4 | bool rev; 5 | Node(int t = 0); 6 | inline bool dir(void) {return p->ch[1] == this;} 7 | inline void SetC(Node *x, bool d) { 8 | ch[d] = x; x->p = this; 9 | } 10 | inline void Rev(void) { 11 | swap(ch[0], ch[1]); rev ^= 1; 12 | } 13 | inline void Push(void) { 14 | if (rev) { 15 | ch[0]->Rev(); 16 | ch[1]->Rev(); 17 | rev = 0; 18 | } 19 | } 20 | inline void Update(void) { 21 | size = ch[0]->size + ch[1]->size + 1; 22 | } 23 | }Tnull, *null = &Tnull, *fim[MAXN]; 24 | // 要记得额外更新null的信息 25 | Node::Node(int _value){ch[0] = ch[1] = p = null; rev = 0;} 26 | inline bool isRoot(Node *x) {return x->p == null || (x != x->p->ch[0] && x != x->p->ch[1]);} 27 | inline void rotate(Node *x) { 28 | Node *p = x->p; bool d = x->dir(); 29 | p->Push(); x->Push(); 30 | if (!isRoot(p)) p->p->SetC(x, p->dir()); else x->p = p->p; 31 | p->SetC(x->ch[!d], d); 32 | x->SetC(p, !d); 33 | p->Update(); 34 | } 35 | inline void splay(Node *x) { 36 | x->Push(); 37 | while (!isRoot(x)) { 38 | if (isRoot(x->p)) rotate(x); 39 | else { 40 | if (x->dir() == x->p->dir()) {rotate(x->p); rotate(x);} 41 | else {rotate(x); rotate(x);} 42 | } 43 | } 44 | x->Update(); 45 | } 46 | inline Node* Access(Node *x) { 47 | Node *t = x, *q = null; 48 | for (; x != null; x = x->p) { 49 | splay(x); x->ch[1] = q; q = x; 50 | } 51 | splay(t); //info will be updated in the splay; 52 | return q; 53 | } 54 | inline void Evert(Node *x) { 55 | Access(x); x->Rev(); 56 | } 57 | inline void link(Node *x, Node *y) { 58 | Evert(x); x->p = y; 59 | } 60 | inline Node* getRoot(Node *x) { 61 | Node *tmp = x; 62 | Access(x); 63 | while (tmp->Push(), tmp->ch[0] != null) tmp = tmp->ch[0]; 64 | splay(tmp); 65 | return tmp; 66 | } 67 | // 一定要确定x和y之间有边 68 | inline void cut(Node *x, Node *y) { 69 | Access(x); splay(y); 70 | if (y->p != x) swap(x, y); 71 | Access(x); splay(y); 72 | y->p = null; 73 | } 74 | inline Node* getPath(Node *x, Node *y) { 75 | Evert(x); Access(y); 76 | return y; 77 | } 78 | inline void clear(void) { 79 | null->rev = 0; null->sie = 0; null->value = 0; 80 | } 81 | -------------------------------------------------------------------------------- /data-structures/link-cut-tree/remark.tex: -------------------------------------------------------------------------------- 1 | 动态树模板,如果维护的权值在边上,需要把边拆成点。 2 | -------------------------------------------------------------------------------- /data-structures/splay/main.cpp: -------------------------------------------------------------------------------- 1 | // 最大可能的节点数 2 | const int MAXN = 100010; 3 | // 每个打标记的操作就是更新这个节点的信息,然后对子节点打标记 4 | struct Node { 5 | Node *ch[2], *p; int size, value; 6 | bool rev; 7 | inline bool dir(void) {return p->ch[1] == this;} 8 | inline void SetC(Node *x, bool d) { 9 | ch[d] = x; x->p = this; 10 | } 11 | inline void Rev(void) { 12 | swap(ch[0], ch[1]); rev ^= 1; 13 | } 14 | // null永远不会push 15 | inline void Push(void) { 16 | if (rev) { 17 | ch[0]->Rev(); 18 | ch[1]->Rev(); 19 | rev = 0; 20 | } 21 | } 22 | // null永远不会update 23 | inline void Update(void) { 24 | size = ch[0]->size + ch[1]->size + 1; 25 | } 26 | inline void initInfo(void) { 27 | rev = 0; 28 | } 29 | }Tnull, *null = &Tnull, *data, POOL[MAXN]; 30 | class Splay {public: 31 | Node *root; 32 | inline void rotate(Node *x) { 33 | Node *p = x->p; bool d = x->dir(); 34 | p->Push(); x->Push(); 35 | p->p->SetC(x, p->dir()); 36 | p->SetC(x->ch[!d], d); 37 | x->SetC(p, !d); 38 | p->Update(); 39 | } 40 | inline void splay(Node *x, Node *G) { 41 | if (G == null) root = x; 42 | while (x->p != G) { 43 | if (x->p->p == G) rotate(x); else { 44 | if (x->dir() == x->p->dir()) {rotate(x->p); rotate(x);} 45 | else {rotate(x); rotate(x);} 46 | } 47 | } 48 | x->Push(); 49 | x->Update(); 50 | } 51 | inline Node* Renew(int value) { 52 | Node *ret = data++; 53 | ret->ch[0] = ret->ch[1] =ret->p = null; 54 | ret->size = 1; ret->value = value; 55 | ret->initInfo(); 56 | return ret; 57 | } 58 | inline Node* getMin(Node *x) {Node *tmp = x; while (tmp->ch[0] != null) tmp = tmp->ch[0]; return tmp;} 59 | inline Node* getMax(Node *x) {Node *tmp = x; while (tmp->ch[1] != null) tmp = tmp->ch[1]; return tmp;} 60 | // 查询第k大 61 | inline Node* getKth(int k) { 62 | Node *tmp = root; 63 | assert(k > 0 && k <= root->size); 64 | while (true) { 65 | tmp->Push(); 66 | if (tmp->ch[0]->size + 1 == k) return tmp; 67 | if (tmp->ch[0]->size >= k) tmp = tmp->ch[0]; 68 | else k -= tmp->ch[0]->size + 1, tmp = tmp->ch[1]; 69 | } 70 | } 71 | // 以下为splay当作平衡树使用 72 | // 查找树中value = v的元素, 返回之后splay 73 | inline Node* find(int v) { 74 | Node *tmp = root; 75 | while (tmp != null) { 76 | tmp->Push(); 77 | if (tmp->value == v) return tmp; 78 | if (v < tmp->value) tmp = tmp->ch[0]; else tmp = tmp->ch[1]; 79 | } 80 | return null; 81 | } 82 | // 统计有多少元素小于等于v, 当flag = 1时,统计多少元素严格小于v, 一定要记得splay最后的那个tmp 83 | inline int Count(int v, bool flag = 0) { 84 | Node *tmp = root, *last = null; int ret = 0; 85 | while (tmp != null) { 86 | tmp->Push(); 87 | last = tmp; 88 | if ((!flag && tmp->value > v) || (flag && tmp->value >= v)) { 89 | tmp = tmp->ch[0]; 90 | } else ret += tmp->ch[0]->size + 1, tmp = tmp->ch[1]; 91 | } 92 | if (last != null) splay(last, null); 93 | return ret; 94 | } 95 | // 删除x这个结点 96 | inline void erase(Node* x) { 97 | splay(x, null); 98 | if (x->ch[0] == null || x->ch[1] == null) { 99 | int d = x->ch[1] != null; 100 | root = x->ch[d]; root->p = null; 101 | return; 102 | } 103 | Node *L = getMax(x->ch[0]), *R = getMax(x->ch[1]); 104 | splay(L, x); splay(R, x); 105 | L->SetC(R, 1); L->p = null; root = L; 106 | L->Update(); 107 | } 108 | // 插入一个值为value的节点,初始要以Insert(root, null, value)来调用, 返回之后splay 109 | inline Node* Insert(Node *&now, Node* father, int value) { 110 | if (now == null) { 111 | now = Renew(value); now->p = father; 112 | return now; 113 | } 114 | Node *ret; 115 | now->Push(); 116 | if (value <= now->value) ret = Insert(now->ch[0], now, value); 117 | else ret = Insert(now->ch[1], now, value); 118 | now->Update(); 119 | return ret; 120 | } 121 | // 以下为splay维护序列, 初始要在原序列中放入一个-inf和inf来防止边界条件 122 | // 得到原数列中[l,r]区间对应的结点,如果l == r + 1则表示是一个空区间 123 | inline Node* getInterval(int l, int r) { 124 | assert(l <= r + 1); 125 | Node *L = getKth(l), *R = getKth(r + 2); 126 | splay(L, null); splay(R, L); 127 | return R->ch[0]; 128 | } 129 | // 删除一段区间[l,r] 130 | inline void eraseInterval(int l, int r) { 131 | getInterval(l, r); 132 | root->ch[1]->ch[0] = null; 133 | root->ch[1]->Update(); 134 | root->Update(); 135 | } 136 | // 在位置l的后面插入一段区间x (0 <= l <= n) 137 | inline void insertInterval(int l, Node *x) { 138 | Node *L = getKth(l + 1), *R = getKth(l + 2); 139 | splay(L, null); splay(R, L); 140 | R->SetC(x, 0); 141 | R->Update(); L->Update(); 142 | } 143 | // 把数列a的[l,r]构建为一个splay 144 | inline Node* Build(int l, int r, int a[]) { 145 | if (l > r) return null; 146 | int mid = (l + r) >> 1; 147 | Node *ret = Renew(a[mid]); 148 | if (l == r) return ret; 149 | ret->SetC(Build(l, mid - 1, a), 0); 150 | ret->SetC(Build(mid + 1, r, a), 1); 151 | ret->Update(); 152 | return ret; 153 | } 154 | }T; 155 | void clear(void) { 156 | data = POOL; 157 | T.root = null; 158 | } 159 | -------------------------------------------------------------------------------- /data-structures/splay/remark.tex: -------------------------------------------------------------------------------- 1 | splay树模板,分为两个部分,第一个部分为splay当作平衡树使用,第二个部分为splay当作线段树维护序列使用。注意在每次Insert之后都要splay其返回值到根 2 | -------------------------------------------------------------------------------- /data-structures/suffix-array/main.cpp: -------------------------------------------------------------------------------- 1 | // string is 1-base, sa is 1-base 2 | int w[MAXM]; 3 | inline void Sort(int a[], int ret[], int n, int m = MAXM - 1) { 4 | for (int i = 0; i <= m; i++) w[i] = 0; 5 | for (int i = 1; i <= n; i++) w[a[i]]++; 6 | for (int i = 1; i <= m; i++) w[i] += w[i - 1]; 7 | for (int i = n; i >= 1; i--) ret[w[a[i]]--] = i; 8 | } 9 | int wa[MAXN], wb[MAXN], tmp[MAXN]; 10 | inline void getSA(int ch[], int sa[], int n) { 11 | int *x = wa, *y = wb; 12 | for (int i = 1; i <= n; i++) x[i] = ch[i]; 13 | Sort(ch, sa, n); 14 | for (int j = 1, p = 1, m = MAXN - 1; p < n; m = p, j <<= 1) { 15 | p = 0; 16 | for (int i = n - j + 1; i <= n; i++) y[++p] = i; 17 | for (int i = 1; i <= n; i++) if (sa[i] > j) y[++p] = sa[i] - j; 18 | for (int i = 1; i <= n; i++) tmp[i] = x[y[i]]; 19 | Sort(tmp, sa, n, m); 20 | for (int i = 1; i <= n; i++) sa[i] = y[sa[i]]; 21 | swap(x, y); x[sa[1]] = p = 1; 22 | for (int i = 2; i <= n; i++) { 23 | if (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + j] == y[sa[i - 1] + j]) x[sa[i]] = p; 24 | else x[sa[i]] = ++p; 25 | } 26 | } 27 | sa[0] = n + 1; // for calculate height. 28 | } 29 | int rank[MAXN]; 30 | inline void getHeight(int ch[], int sa[], int height[], int n) { 31 | for (int i = 1; i <= n; i++) rank[sa[i]] = i; 32 | for (int i = 1, t = 0; i <= n; i++) { 33 | if (t > 0) t--; 34 | while (ch[i + t] == ch[sa[rank[i] - 1] + t]) t++; 35 | height[rank[i]] = t; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data-structures/suffix-array/remark.tex: -------------------------------------------------------------------------------- 1 | 倍增算法构建后缀数组,时间复杂度为 $O(n \lg n)$. 注意字符串和最终的后缀数组均从 1 开始编号. 要保证字符串中都为大于0的字符, 而且字符串的第n+1位应该为0. 2 | 3 | -------------------------------------------------------------------------------- /data-structures/suffix-automaton/main.cpp: -------------------------------------------------------------------------------- 1 | struct Node { 2 | Node *next[26], *par; int val, end; // 26 is volatile 3 | }POOL[MAXN << 1], *data, *root, *last; //Note that the size of POOL should be doubled. 4 | inline void Add(int x) { 5 | Node *p = last, *np = data++; 6 | np->val = p->val + 1; np->end = true; 7 | while (p && !p->next[x]) 8 | p->next[x] = np, p = p->par; 9 | if (p == 0) { 10 | np->par = root; 11 | } else { 12 | Node *q = p->next[x]; 13 | if (q->val == p->val + 1) { 14 | np->par = q; 15 | } else { 16 | Node *nq = data++; 17 | nq->val = p->val + 1; 18 | memcpy(nq->next, q->next, sizeof q->next); 19 | nq->par = q->par; 20 | np->par = q->par = nq; 21 | while (p && p->next[x] == q) 22 | p->next[x] = nq, p = p->par; 23 | } 24 | } 25 | last = np; 26 | } 27 | void Clear(void) { 28 | data = POOL; last = root = data++; 29 | } 30 | -------------------------------------------------------------------------------- /data-structures/suffix-automaton/remark.tex: -------------------------------------------------------------------------------- 1 | 构建后缀自动机。统计一个串出现的次数时,只需统计其节点所对应的子树中,end为true的节点的个数即可。将所有节点按val值从小到大排序后即可得到parent树由根开始的BFS序。 2 | -------------------------------------------------------------------------------- /graph/Hopcroft-Karp/main.cpp: -------------------------------------------------------------------------------- 1 | const int MAXN = 10000; 2 | int match[MAXN]; 3 | int levelx[MAXN], levely[MAXN], link[MAXN]; 4 | int d[MAXN]; 5 | inline bool Bfs(void) { 6 | int head = 1, tail = 0; 7 | memset(levely, 0, sizeof levely); 8 | for (int i = 1; i <= n; i++) { 9 | if (!match[i]) d[++tail] = i; 10 | levelx[i] = 0; 11 | } 12 | bool ret = false; 13 | while (head <= tail) { 14 | int now = d[head++]; 15 | for (Edge *p = a[now]; p; p = p->next) if (levely[p->y] == 0) { 16 | levely[p->y] = levelx[now] + 1; 17 | if (link[p->y] == 0) ret = true; 18 | else levelx[link[p->y]] = levely[p->y] + 1, d[++tail] = link[p->y]; 19 | } 20 | } 21 | return ret; 22 | } 23 | bool Find(int u) { 24 | for (Edge *p = a[u]; p; p = p->next) if (levely[p->y] == levelx[u] + 1) { 25 | levely[p->y] = 0; 26 | if (link[p->y] == 0 || Find(link[p->y])) { 27 | match[u] = p->y; link[p->y] = u; 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | inline void Match(void) { 34 | while (Bfs()) 35 | for (int i = 1; i <= n; i++) 36 | if (!match[i]) Find(i); 37 | } 38 | inline void clear(void) { 39 | memset(match, 0, sizeof match); 40 | memset(link, 0, sizeof link); 41 | memset(levelx, 0, sizeof levelx); 42 | memset(levely, 0, sizeof levely); 43 | } 44 | -------------------------------------------------------------------------------- /graph/Hopcroft-Karp/remark.tex: -------------------------------------------------------------------------------- 1 | Hopcroft 算法,其中二分图左边点的编号为1-n,右边点编号为n+1 - N,match[i]表示左边的i匹配上的右边的点,0无匹配,link[i]表示右边的i匹配上的左边的点,0无匹配 2 | -------------------------------------------------------------------------------- /graph/KM-algorithm/main.cpp: -------------------------------------------------------------------------------- 1 | const int MAXN = 210; 2 | int nx, ny; // 左边的点标号从1到nx,右边点标号从1到ny 3 | long long inf, cost[MAXN][MAXN], fx[MAXN], fy[MAXN], dist[MAXN]; //权值若为long long的话,只需改动此行即可 4 | int used[MAXN], maty[MAXN], which[MAXN]; 5 | inline void AddEdge(int x, int y, int z) { 6 | cost[x][y] = z; 7 | } 8 | pair KM(void) { 9 | for (int x = 1; x <= nx; x++) { 10 | int y0 = 0; maty[0] = x; 11 | for (int y = 0; y <= ny; y++) { dist[y] = inf + 1; used[y] = false; } 12 | do { 13 | used[y0] = true; 14 | int x0 = maty[y0], y1; 15 | long long delta = inf + 1; 16 | for (int y = 1; y <= ny; y++) if (!used[y]) { 17 | long long curdist = cost[x0][y] - fx[x0] - fy[y]; 18 | if (curdist < dist[y]) { 19 | dist[y] = curdist; 20 | which[y] = y0; 21 | } 22 | if (dist[y] < delta) { 23 | delta = dist[y]; 24 | y1 = y; 25 | } 26 | } 27 | for (int y = 0; y <= ny; y++) if (used[y]) { 28 | fx[maty[y]] += delta; 29 | fy[y] -= delta; 30 | } else dist[y] -= delta; 31 | y0 = y1; 32 | } while (maty[y0] != 0); 33 | do { 34 | int y1 = which[y0]; 35 | maty[y0] = maty[y1]; 36 | y0 = y1; 37 | } while (y0); 38 | } 39 | long long ret = 0; 40 | int npair = 0; 41 | for (int y = 1; y <= ny; y++) { 42 | int x = maty[y]; 43 | if (cost[x][y] < inf) { 44 | ret += cost[x][y]; 45 | npair++; 46 | } 47 | } 48 | return make_pair(npair, ret); 49 | } 50 | inline void clear(void) { 51 | memset(fx, 0x9f, sizeof fx); 52 | memset(fy, 0x9f, sizeof fy); 53 | memset(cost, 0x3f, sizeof cost); 54 | memset(maty, 0, sizeof maty); 55 | inf = cost[0][0]; 56 | } 57 | -------------------------------------------------------------------------------- /graph/KM-algorithm/remark.tex: -------------------------------------------------------------------------------- 1 | KM算法求最小权完美匹配(注意不是全局最大权匹配),如果两边点数不相等或者非完美二分图,则可以添加虚拟点和权值为inf的边 2 | 3 | 若求最大权匹配,则将边权取反即可 4 | -------------------------------------------------------------------------------- /graph/Maximum-Flow/main.cpp: -------------------------------------------------------------------------------- 1 | struct Edge { 2 | int y, f; Edge *next, *opt; 3 | }*a[MAXN], DATA[MAXM << 1], *data = DATA; 4 | inline void Add(int x, int y, int c) { 5 | Edge *tmp = data++; 6 | tmp->y = y; tmp->f = c, tmp->next = a[x]; a[x] = tmp; 7 | tmp = data++; tmp->y = x; tmp->f = 0; tmp->next = a[y]; a[y] = tmp; 8 | a[x]->opt = a[y]; a[y]->opt = a[x]; 9 | } 10 | int n, m, vs, vt, L; 11 | int level[MAXN], d[MAXN]; 12 | inline bool Bfs(void) { 13 | memset(level, -1, sizeof level); 14 | d[1] = vs; level[vs] = 0; 15 | int head = 1, tail = 1; 16 | while (head <= tail) { 17 | int now = d[head++]; 18 | e[now] = a[now]; 19 | for (Edge *p = a[now]; p; p = p->next) if (p->f > 0&& level[p->y] == -1) 20 | level[d[++tail] = p->y] = level[now] + 1; 21 | } 22 | return level[vt] != -1; 23 | } 24 | inline int Extend(int u, int sum) { 25 | if (u == vt) return sum; 26 | int r = 0, t; 27 | for (Edge *p = e[u]; p && r < sum; p = p->next) if (level[p->y] == level[u] + 1 && p->f > 0) { 28 | t = std::min(sum - r, p->f); 29 | t = Extend(p->y, t); 30 | p->f -= t, p->opt->f += t, r += t; 31 | e[u] = p; 32 | } 33 | if (!r) level[u] = -1; 34 | return r; 35 | } 36 | inline int Dinic(void) { 37 | int r = 0, t; 38 | while (Bfs()) { 39 | while ((t = Extend(vs, inf))) r += t; 40 | } 41 | return r; 42 | } 43 | -------------------------------------------------------------------------------- /graph/Maximum-Flow/remark.tex: -------------------------------------------------------------------------------- 1 | Dinic求解最大流,有当前弧优化 2 | -------------------------------------------------------------------------------- /graph/Minimum-cost-Flow/main.cpp: -------------------------------------------------------------------------------- 1 | struct Edge { 2 | int y, f, c; Edge *next, *opt; 3 | Edge(int y, int f, int c, Edge *next):y(y), f(f), c(c), next(next){} 4 | }*a[MAXN]; 5 | inline void AddEdge(int x, int y, int f, int c) { 6 | a[x] = new Edge(y, f, c, a[x]); 7 | a[y] = new Edge(x, 0, -c, a[y]); 8 | a[x]->opt = a[y]; a[y]->opt = a[x]; 9 | } 10 | int d[MAXN], vis[MAXN], dis[MAXN]; Edge *path[MAXN]; 11 | inline pair Spfa(void) { 12 | int Flow = 0, Cost = 0; 13 | while (true) { 14 | memset(vis, 0, sizeof vis); 15 | memset(dis, 0x7f, sizeof dis); 16 | memset(path, 0, sizeof path); 17 | int head = 1, tail = 1, sum = 1; d[1] = vs; vis[vs] = true; dis[vs] = 0; 18 | while (sum) { 19 | int now = d[head++]; if (head == MAXN) head = 1; sum--; 20 | for (Edge *p = a[now]; p; p = p->next) if (p->f > 0 && dis[p->y] > dis[now] + p->c) { 21 | dis[p->y] = dis[now] + p->c; 22 | path[p->y] = p; 23 | if (!vis[p->y]) { 24 | ++tail; if (tail == MAXN) tail = 1; sum++; 25 | d[tail] = p->y; 26 | vis[p->y] = true; 27 | } 28 | } 29 | vis[now] = false; 30 | } 31 | if (dis[vt] == dis[0]) return make_pair(Flow, Cost); 32 | int tmp = vt, Min = ~0U>>1; 33 | while (path[tmp]) { 34 | Min = min(Min, path[tmp]->f); 35 | tmp = path[tmp]->opt->y; 36 | } 37 | Flow += Min; 38 | tmp = vt; 39 | while (path[tmp]) { 40 | path[tmp]->f -= Min; 41 | path[tmp]->opt->f += Min; 42 | Cost += Min * path[tmp]->c; 43 | tmp = path[tmp]->opt->y; 44 | } 45 | } 46 | return make_pair(Flow, Cost); 47 | } 48 | -------------------------------------------------------------------------------- /graph/Minimum-cost-Flow/remark.tex: -------------------------------------------------------------------------------- 1 | spfa求解最小费用最大流 2 | -------------------------------------------------------------------------------- /graph/get-bridge/main.cpp: -------------------------------------------------------------------------------- 1 | int dfs[MAXN], low[MAXN], tim = 0; 2 | inline void Dfs(int u) { 3 | dfs[u] = low[u] = ++tim; 4 | for (Edge *p = a[u]; p; p = p->next) if (p->flag) { 5 | if (!dfs[p->y]) { 6 | p->opt->flag = false; 7 | Dfs(p->y); 8 | low[u] = min(low[u], low[p->y]); 9 | } else low[u] = min(low[u], dfs[p->y]); 10 | } 11 | for (Edge *p = a[u]; p; p = p->next) if (p->opt->flag == false && low[p->y] > dfs[u]) { 12 | p->bridge = p->opt->bridge = true; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /graph/get-bridge/remark.tex: -------------------------------------------------------------------------------- 1 | Tarjan 算法求一个无向图的桥 2 | -------------------------------------------------------------------------------- /graph/最小树形图/main.cpp: -------------------------------------------------------------------------------- 1 | typedef pair PII; 2 | PII tmp[MAXN]; 3 | int dis[MAXN][MAXN]; int map[MAXN][MAXN]; 4 | int vis[MAXN], q[MAXN], inp[MAXN]; 5 | int Dfs(int u) { 6 | vis[u] = true; int ret = 1; 7 | for (int i = 1; i <= n; i++) if (map[u][i] && !vis[i]) ret += Dfs(i); 8 | return ret; 9 | } 10 | int Zhuliu(void) { 11 | memset(map, 0, sizeof map); 12 | memset(vis, 0, sizeof vis); 13 | if (Dfs(1) != n) return -1; 14 | int done = 0; 15 | while (true) { 16 | memset(vis, 0, sizeof vis); memset(inp, 0, sizeof inp); 17 | int Ans = 0; 18 | for (int i = 1; i <= n; i++) vis[i] = i; 19 | for (int i = 2; i <= n; i++) { 20 | tmp[i] = PII(1000000000, 0); 21 | for (int j = 1; j <= n; j++) if (map[j][i] == 1) tmp[i] = min(tmp[i], PII(dis[j][i], j)); 22 | inp[tmp[i].second]++; if (tmp[i].second) Ans += tmp[i].first; 23 | } 24 | int head = 1, tail = 0; 25 | for (int i = 1; i <= n; i++) if (!inp[i]) q[++tail] = i; 26 | while (head <= tail) { 27 | int now = q[head++]; 28 | if (!--inp[tmp[now].second]) q[++tail] = tmp[now].second; 29 | } 30 | bool ok = true; 31 | for (int i = 1, t; i <= n; i++) if (inp[i] > 0) { 32 | t = i; ok = false; 33 | do { 34 | inp[t] = -i; 35 | t = tmp[t].second; 36 | vis[t] = i; 37 | } while (t != i); 38 | } 39 | if (ok) return Ans + done; 40 | for (int i = 1; i <= n; i++) if (inp[i] < 0) { 41 | done += tmp[i].first; 42 | for (int j = 1; j <= n; j++) if (vis[j] != vis[i]) { 43 | if (map[i][j]) { 44 | map[vis[i]][vis[j]] = 1; 45 | dis[vis[i]][vis[j]] = min(dis[vis[i]][vis[j]], dis[i][j]); 46 | } 47 | if (map[j][i]) { 48 | dis[vis[j]][vis[i]] = min(dis[vis[j]][vis[i]], dis[j][i] - tmp[i].first); 49 | map[vis[j]][vis[i]] = 1; 50 | } 51 | } 52 | if (vis[i] != i) for (int j = 1; j <= n; j++) map[i][j] = map[j][i] = 0; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /graph/最小树形图/remark.tex: -------------------------------------------------------------------------------- 1 | 最小树形图算法,使用邻接矩阵存图,返回-1表示无最小树形图。否则,tmp[i].second表示i的入边 2 | map[i][j] = 1 表示表示存在一条从i到j的有向边 3 | dis[i][j] 表示从i到j边的长度 4 | -------------------------------------------------------------------------------- /math/geometry/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | const double eps = 1e-13; 7 | const double pi = 3.14159265358979324; 8 | struct point2 { 9 | double x, y; 10 | point2& operator += (point2 a) { x+=a.x, y+=a.y; return *this; } 11 | point2& operator -= (point2 a) { x-=a.x, y-=a.y; return *this; } 12 | point2& operator *= (double a) { x*=a, y*=a; return *this; } 13 | point2& operator /= (double a) { x/=a, y/=a; return *this; } 14 | }; 15 | point2 operator + (point2 a, point2 b) { point2 c(a); c += b; return c; } 16 | point2 operator - (point2 a, point2 b) { point2 c(a); c -= b; return c; } 17 | point2 operator * (point2 a, double b) { point2 c(a); c *= b; return c; } 18 | point2 operator * (double a, point2 b) { point2 c(b); c *= a; return c; } 19 | point2 operator / (point2 a, double b) { point2 c(a); c /= b; return c; } 20 | double operator * (point2 a, point2 b) { return a.x*b.x+a.y*b.y; } 21 | double operator % (point2 a, point2 b) { return a.x*b.y-a.y*b.x; } 22 | double dis(point2 a) { return sqrt(a.x * a.x + a.y * a.y); } 23 | double arg(point2 a) { return atan2(a.y, a.x); } 24 | point2 rotate(point2 a, double th) { 25 | point2 b; 26 | b.x = a.x * cos(th) - a.y * sin(th); 27 | b.y = a.x * sin(th) + a.y * cos(th); 28 | return b; 29 | } 30 | int parallel(point2 a, point2 b) { 31 | return a * a < eps * eps || b * b < eps * eps 32 | || (a % b) * (a % b) / ((a * a) * (b * b)) < eps * eps; 33 | } 34 | int perpend(point2 a, point2 b) { 35 | return a * a < eps * eps || b * b < eps * eps 36 | || (a * b) * (a * b) / ((a * a) * (b * b)) < eps * eps; 37 | } 38 | struct line2 { 39 | point2 a, s; 40 | }; 41 | struct circle2 { 42 | point2 a; 43 | double r; 44 | }; 45 | double point_line_dis(point2 a, line2 b, point2 *res = NULL) { 46 | point2 p; 47 | p = b.a + ((a - b.a) * b.s) / (b.s * b.s) * b.s; 48 | if (res != NULL) *res = p; 49 | return dis(a - p); 50 | } 51 | int line_line_cross(line2 a, line2 b, point2 *res = NULL) { 52 | if (parallel(a.s, b.s)) 53 | if (parallel(b.a - a.a, a.s)) 54 | return -1; 55 | else 56 | return 0; 57 | double k1 = (b.a - a.a) % b.s / (a.s % b.s); 58 | if (res != NULL) *res = a.a + k1 * a.s; 59 | return 1; 60 | } 61 | int segment_segment_cross(line2 a, line2 b, point2 *res = NULL) { 62 | if (a.s * a.s < eps * eps && b.s * b.s < eps * eps) 63 | if ((b.a - a.a) * (b.a - a.a) < eps * eps) { 64 | if (res != NULL) *res = a.a; 65 | return 1; 66 | } else 67 | return 0; 68 | if (parallel(a.s, b.s) && parallel(b.a - a.a, a.s) && parallel(a.a - b.a, b.s)) { 69 | double y1, y2, y3, y4; 70 | point2 y1p = a.a, y2p = a.a + a.s, y3p = b.a, y4p = b.a + b.s; 71 | if (std::abs(a.s.x) < std::abs(a.s.y) || std::abs(b.s.x) < std::abs(b.s.y)) 72 | y1 = y1p.y, y2 = y2p.y, y3 = y3p.y, y4 = y4p.y; 73 | else 74 | y1 = y1p.x, y2 = y2p.x, y3 = y3p.x, y4 = y4p.x; 75 | if (y1 > y2) std::swap(y1, y2), std::swap(y1p, y2p); 76 | if (y3 > y4) std::swap(y3, y4), std::swap(y3p, y4p); 77 | if (y2 - y1 < y4 - y3) std::swap(y1, y3), std::swap(y1p, y3p), std::swap(y2, y4), std::swap(y2p, y4p); 78 | if (y3 > y2 + (y2 - y1) * eps || y4 < y1 - (y2 - y1) * eps) 79 | return 0; 80 | else if (fabs(y3 - y2) < (y2 - y1) * eps || fabs(y3 - y4) < eps) { 81 | if (res != NULL) *res = y3p; 82 | return 1; 83 | } else if (fabs(y4 - y1) < (y2 - y1) * eps || fabs(y1 - y2) < eps) { 84 | if (res != NULL) *res = y1p; 85 | return 1; 86 | } else 87 | return -1; 88 | } else { 89 | double k1 = (b.a - a.a) % a.s, k2 = (b.a + b.s - a.a) % a.s; 90 | k1 /= a.s * a.s, k2 /= a.s * a.s; 91 | double k3 = (a.a - b.a) % b.s, k4 = (a.a + a.s - b.a) % b.s; 92 | k3 /= b.s * b.s, k4 /= b.s * b.s; 93 | int ret = (k1 < eps && k2 > -eps || k1 > -eps && k2 < eps) 94 | && (k3 < eps && k4 > -eps || k3 > -eps && k4 < eps); 95 | if (ret) line_line_cross(a, b, res); 96 | return ret; 97 | } 98 | } 99 | int line_circle_cross(line2 a, circle2 b, point2 *res1 = NULL, point2 *res2 = NULL) { 100 | point2 p; 101 | double d = point_line_dis(b.a, a, &p); 102 | if (d / b.r > 1 + eps) 103 | return 0; 104 | else if (d / b.r > 1 - eps) { 105 | if (res1 != NULL) *res1 = p; 106 | return 1; 107 | } else { 108 | d = sqrt(b.r * b.r - d * d) / dis(a.s); 109 | if (res1 != NULL) *res1 = p + d * a.s; 110 | if (res2 != NULL) *res2 = p - d * a.s; 111 | return 2; 112 | } 113 | } 114 | int circle_circle_cross(circle2 a, circle2 b, point2 *res1 = NULL, point2 *res2 = NULL) { 115 | double d = dis(a.a - b.a); 116 | point2 u = (b.a - a.a) / d; 117 | if (d / (a.r + b.r) > 1 + eps) 118 | return 0; 119 | else if (d / (a.r + b.r) > 1 - eps) { 120 | if (res1 != NULL) *res1 = a.a + u * a.r; 121 | return 1; 122 | } else if ((d - fabs(a.r - b.r)) / (a.r + b.r) > eps) { 123 | double th = acos((a.r * a.r + d * d - b.r * b.r) / (2 * a.r * d)); 124 | if (res1 != NULL) *res1 = a.a + rotate(u * a.r, th); 125 | if (res2 != NULL) *res2 = a.a + rotate(u * a.r, -th); 126 | return 2; 127 | } else if ((d - fabs(a.r - b.r)) / (a.r + b.r) > -eps) { 128 | if (a.r / b.r < 1 - eps) { 129 | if (res1 != NULL) *res1 = b.a - u * b.r; 130 | return 1; 131 | } else if (a.r / b.r > 1 + eps) { 132 | if (res1 != NULL) *res1 = a.a + u * a.r; 133 | return 1; 134 | } else return -1; 135 | } else 136 | return 0; 137 | } 138 | int point_circle_tangent(point2 a, circle2 b, point2 *res1 = NULL, point2 *res2 = NULL) { 139 | double d = dis(a - b.a); 140 | point2 u = (a - b.a) / d; 141 | if (d / b.r > 1 + eps) { 142 | double th = acos(b.r / d); 143 | if (res1 != NULL) *res1 = b.a + rotate(u * b.r, th); 144 | if (res2 != NULL) *res2 = b.a + rotate(u * b.r, -th); 145 | return 2; 146 | } else if (d / b.r > 1 - eps) { 147 | if (res1 != NULL) *res1 = a; 148 | return 1; 149 | } else 150 | return 0; 151 | } 152 | int circle_circle_tangent(circle2 a, circle2 b, line2 *reso1 = NULL, line2 *reso2 = NULL, line2 *resi1 = NULL, line2 *resi2 = NULL) { 153 | double d = dis(a.a - b.a); 154 | point2 u = (b.a - a.a) / d; 155 | int cnt = 0; 156 | if ((d - fabs(a.r - b.r)) / (a.r + b.r) > eps) { 157 | double th = acos((a.r - b.r) / d); 158 | if (reso1 != NULL) { 159 | reso1->a = a.a + rotate(u * a.r, th); 160 | reso1->s = b.a + rotate(u * b.r, th) - reso1->a; 161 | } 162 | if (reso2 != NULL) { 163 | reso2->a = a.a + rotate(u * a.r, -th); 164 | reso2->s = b.a + rotate(u * b.r, -th) - reso2->a; 165 | } 166 | cnt += 2; 167 | } else if ((d - fabs(a.r - b.r)) / (a.r + b.r) > -eps) { 168 | if (a.r / b.r < 1 - eps) { 169 | if (reso1 != NULL) { 170 | reso1->a = b.a - u * b.r; 171 | reso1->s = rotate(u, pi / 2); 172 | } 173 | cnt++; 174 | } else if (a.r / b.r > 1 + eps) { 175 | if (reso1 != NULL) { 176 | reso1->a = a.a + u * a.r; 177 | reso1->s = rotate(u, pi / 2); 178 | } 179 | cnt++; 180 | } else return -1; 181 | } 182 | if (d / (a.r + b.r) > 1 + eps) { 183 | double th = acos((a.r + b.r) / d); 184 | if (resi1 != NULL) { 185 | resi1->a = a.a + rotate(u * a.r, th); 186 | resi1->s = b.a - rotate(u * b.r, th) - resi1->a; 187 | } 188 | if (resi2 != NULL) { 189 | resi2->a = a.a + rotate(u * a.r, -th); 190 | resi2->s = b.a - rotate(u * b.r, -th) - resi2->a; 191 | } 192 | cnt += 2; 193 | } else if (d / (a.r + b.r) > 1 - eps) { 194 | if (resi1 != NULL) { 195 | resi1->a = a.a + u * a.r; 196 | resi1->s = rotate(u, pi / 2); 197 | } 198 | cnt++; 199 | } 200 | return cnt; 201 | } 202 | typedef std::vector convex2; 203 | double area(const convex2 &a) { 204 | double s = 0; 205 | for (int i = 0; i < a.size(); i++) 206 | s += a[i] % a[(i+1)%a.size()]; 207 | return fabs(s)/2; 208 | } 209 | int point_convex_inside(point2 a, const convex2 &b) { 210 | double sum = 0; 211 | for (int i = 0; i < b.size(); i++) 212 | sum += fabs((b[i] - a) % (b[(i+1)%b.size()] - a)); 213 | return fabs(sum / (2*area(b)) - 1) < eps; 214 | } 215 | int line_convex_cross(line2 a, const convex2 &b, point2 *res1 = NULL, point2 *res2 = NULL) { 216 | int cnt = 0; 217 | for (int i = 0; i < b.size(); i++) { 218 | line2 ltmp; point2 ptmp; 219 | ltmp.a = b[i], ltmp.s = b[(i+1)%b.size()] - b[i]; 220 | int flag = line_line_cross(a, ltmp, &ptmp); 221 | if (flag == -1) return -1; 222 | if (flag == 0) continue; 223 | double k = (ptmp - ltmp.a) * ltmp.s / (ltmp.s * ltmp.s); 224 | if (k < eps || k > 1+eps) continue; 225 | if (cnt == 0 && res1 != NULL) *res1 = ptmp; 226 | else if (cnt == 1 && res2 != NULL) *res2 = ptmp; 227 | cnt++; 228 | } 229 | return cnt; 230 | } 231 | int convex_gen_cmp(point2 a, point2 b) { 232 | return a.y < b.y - eps || fabs(a.y - b.y) < eps && a.x < b.x - eps; 233 | } 234 | int convex_gen(const convex2 &a, convex2 &b) { 235 | std::deque q; 236 | convex2 t(a); 237 | std::sort(t.begin(), t.end(), convex_gen_cmp); 238 | q.push_back(t[0]), q.push_back(t[1]); 239 | for (int i = 2; i < t.size(); i++) { 240 | while (q.size() > 1) { 241 | point2 p1 = t[i]-q[q.size()-1], p2 = q[q.size()-1]-q[q.size()-2]; 242 | if (p1 % p2 > 0 || parallel(p1, p2)) q.pop_back(); 243 | else break; 244 | } 245 | q.push_back(t[i]); 246 | } 247 | int pretop = q.size(); 248 | for (int i = t.size()-1; i >= 0; i--) { 249 | while (q.size() > pretop) { 250 | point2 p1 = t[i]-q[q.size()-1], p2 = q[q.size()-1]-q[q.size()-2]; 251 | if (p1 % p2 > 0 || parallel(p1, p2)) q.pop_back(); 252 | else break; 253 | } 254 | q.push_back(t[i]); 255 | } 256 | q.pop_back(); 257 | if (q.size() < 3) { 258 | b.clear(); 259 | return 0; 260 | } 261 | b.clear(); 262 | for (int i = 0; i < q.size(); i++) b.push_back(q[i]); 263 | return 1; 264 | } 265 | int halfplane_cross_cmp(line2 a, line2 b) { 266 | double c1 = arg(a.s), c2 = arg(b.s); 267 | return c1 < c2-eps || fabs(c1-c2) < eps && b.s % (a.a - b.a) / dis(b.s) > eps; 268 | } 269 | int halfplane_cross(const std::vector &a, convex2 &b) { 270 | std::vector t(a); 271 | std::sort(t.begin(), t.end(), halfplane_cross_cmp); 272 | int j = 0; 273 | for (int i = 0; i < t.size(); i++) 274 | if (!i || arg(t[i].s) > arg(t[i-1].s) + eps) t[j++] = t[i]; 275 | if (j > 0 && arg(t[j].s) > arg(t[0].s) + 2*pi - eps) j--; 276 | t.resize(j); 277 | std::deque q; 278 | q.push_back(t[0]), q.push_back(t[1]); 279 | point2 p; 280 | for (int i = 2, k = 0; i < t.size(); i++) { 281 | for (; k < q.size() && t[i].s % q[k].s > 0; k++); 282 | point2 s1 = q[q.size()-1].s, s2 = q[0].s; 283 | if (k > 0 && k < q.size() && s1 % s2 > 0 && !parallel(s1, s2)) { 284 | line_line_cross(q[k], q[k-1], &p); 285 | double r1 = t[i].s % (p - t[i].a) / dis(t[i].s); 286 | line_line_cross(q[0], q[q.size()-1], &p); 287 | double r2 = t[i].s % (p - t[i].a) / dis(t[i].s); 288 | if (r1 < eps && r2 < eps) { 289 | b.clear(); 290 | return 0; 291 | } else if (r1 > -eps && r2 > -eps) 292 | continue; 293 | } 294 | while (q.size() > 1) { 295 | line_line_cross(q[q.size()-1], q[q.size()-2], &p); 296 | if (t[i].s % (p - t[i].a) / dis(t[i].s) < eps) { 297 | q.pop_back(); 298 | if (k == q.size()) k--; 299 | } else break; 300 | } 301 | while (q.size() > 1) { 302 | line_line_cross(q[0], q[1], &p); 303 | if (t[i].s % (p - t[i].a) / dis(t[i].s) < eps) { 304 | q.pop_front(); 305 | k--; if (k < 0) k = 0; 306 | } else break; 307 | } 308 | q.push_back(t[i]); 309 | } 310 | b.clear(); 311 | for (int i = 0; i < q.size(); i++) { 312 | line_line_cross(q[i], q[(i+1)%q.size()], &p); 313 | b.push_back(p); 314 | } 315 | return 1; 316 | } 317 | struct point3 { 318 | double x, y, z; 319 | point3& operator += (point3 a) { x+=a.x,y+=a.y,z+=a.z; return *this; } 320 | point3& operator -= (point3 a) { x-=a.x,y-=a.y,z-=a.z; return *this; } 321 | point3& operator *= (double a) { x*=a, y*=a, z*=a; return *this; } 322 | point3& operator /= (double a) { x/=a, y/=a, z/=a; return *this; } 323 | }; 324 | point3 operator + (point3 a, point3 b) { point3 c(a); c += b; return c; } 325 | point3 operator - (point3 a, point3 b) { point3 c(a); c -= b; return c; } 326 | point3 operator * (point3 a, double b) { point3 c(a); c *= b; return c; } 327 | point3 operator * (double a, point3 b) { point3 c(b); c *= a; return c; } 328 | point3 operator / (point3 a, double b) { point3 c(a); c /= b; return c; } 329 | double operator * (point3 a, point3 b) { return a.x*b.x+a.y*b.y+a.z*b.z; } 330 | point3 operator % (point3 a, point3 b) { 331 | point3 c; 332 | c.x = a.y * b.z - a.z * b.y; 333 | c.y = a.z * b.x - a.x * b.z; 334 | c.z = a.x * b.y - a.y * b.x; 335 | return c; 336 | } 337 | double dis(point3 a) { return sqrt(a.x * a.x + a.y * a.y + a.z * a.z); } 338 | int parallel(point3 a, point3 b) { 339 | return a * a < eps * eps || b * b < eps * eps 340 | || (a % b) * (a % b) / ((a * a) * (b * b)) < eps * eps; 341 | } 342 | int perpend(point3 a, point3 b) { 343 | return a * a < eps * eps || b * b < eps * eps 344 | || (a * b) * (a * b) / ((a * a) * (b * b)) < eps * eps; 345 | } 346 | struct line3 { 347 | point3 a, s; 348 | }; 349 | struct face3 { 350 | point3 a, n; 351 | }; 352 | struct circle3 { 353 | point3 a; 354 | double r; 355 | }; 356 | point2 project(point3 a, face3 b, point3 xs) { 357 | point3 ys; ys = b.n % xs; 358 | point2 c; c.x = ((a - b.a) * xs) / dis(xs), c.y = ((a - b.a) * ys) / dis(ys); 359 | return c; 360 | } 361 | line2 project(line3 a, face3 b, point3 xs) { 362 | line2 c; c.a = project(a.a, b, xs), c.s = project(a.a + a.s, b, xs) - c.a; 363 | return c; 364 | } 365 | point3 revproject(point2 a, face3 b, point3 xs) { 366 | point3 ys; ys = b.n % xs; 367 | return a.x * xs / dis(xs) + a.y * ys / dis(ys) + b.a; 368 | } 369 | line3 revproject(line2 a, face3 b, point3 xs) { 370 | line3 c; c.a = revproject(a.a, b, xs), c.s = revproject(a.a + a.s, b, xs) - c.a; 371 | return c; 372 | } 373 | double point_line_dis(point3 a, line3 b, point3 *res = NULL) { 374 | point3 p; 375 | p = b.a + ((a - b.a) * b.s) / (b.s * b.s) * b.s; 376 | if (res != NULL) *res = p; 377 | return dis(a - p); 378 | } 379 | double point_face_dis(point3 a, face3 b, point3 *res = NULL) { 380 | point3 p; 381 | p = ((a - b.a) * b.n) / (b.n * b.n) * b.n; 382 | if (res != NULL) *res = a - p; 383 | return dis(p); 384 | } 385 | double line_line_dis(line3 a, line3 b, point3 *resa = NULL, point3 *resb = NULL) { 386 | point3 p; 387 | if (parallel(a.s, b.s)) { 388 | double d = point_line_dis(a.a, b, &p); 389 | if (resa != NULL) *resa = a.a; 390 | if (resb != NULL) *resb = p; 391 | return d; 392 | } 393 | point3 n = a.s % b.s; 394 | face3 f; f.a = b.a, f.n = n; 395 | double d = point_face_dis(a.a, f, &p); 396 | double k1 = ((b.a - p) % b.s) * n / (n * n); 397 | if (resb != NULL) *resb = p + k1 * a.s; 398 | if (resa != NULL) *resa = a.a + k1 * a.s; 399 | return d; 400 | } 401 | int line_face_cross(line3 a, face3 b, point3 *res = NULL) { 402 | if (perpend(a.s, b.n)) 403 | if (perpend(b.a - a.a, b.n)) 404 | return -1; 405 | else 406 | return 0; 407 | double k = (b.a - a.a) * b.n / (a.s * b.n); 408 | if (res != NULL) *res = a.a + k * a.s; 409 | return 1; 410 | } 411 | int face_face_cross(face3 a, face3 b, line3 *res = NULL) { 412 | if (parallel(a.n, b.n)) 413 | if (perpend(b.a - a.a, a.n)) 414 | return -1; 415 | else 416 | return 0; 417 | point3 s = a.n % b.n; 418 | point3 p; 419 | line3 t; t.a = a.a, t.s = a.n % s; 420 | line_face_cross(t, b, &p); 421 | if (res != NULL) 422 | res->a = p, res->s = s; 423 | return 1; 424 | } 425 | -------------------------------------------------------------------------------- /math/geometry/remark.tex: -------------------------------------------------------------------------------- 1 | 计算几何模板,包含: 2 | \begin{itemize} 3 | \item point2/3 点/向量,方法:向量加+减-,数乘*,点乘*,叉乘%,模dis,辐角arg(2维),旋转rotate(2维),平行parallel,垂直perpend,三维点根据所在面的法向量和一个面上向量投影为二维点以及反投影 4 | \item line2/3 线,表示为起点+方向向量,方法:点线距和垂足,线线交、线段线段交(线段交可退化,2维),线线距和最近点对(3维,若平行返回任意一对),线的投影(同点) 5 | \item face3 面,表示为起点+法向量,方法:点面距和垂足,线面交,面面交 6 | \item circle2 圆,表示为圆心+半径,方法:线圆交,圆圆交,点到圆的切点,圆与圆的公切线(若公切线有两个切点,返回直线两端点为两切点,否则返回直线端点为切点) 7 | \item convex2 凸包,方法:面积,点不严格在凸包内,线与凸包交,水平序Graham求凸包,半平面交(半平面为传入向量左侧平面) 8 | \end{itemize} 9 | 说明:对需要求点/线的,点/线作为指针传入,若为NULL表示不需要(默认);对需要求点/线个数的,返回值为-1表示有无穷多个。 10 | -------------------------------------------------------------------------------- /math/numbertheory/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | void ext_gcd(int a, int b, int &x, int &y) { 7 | if (!b) x = 1, y = 0; 8 | else if (!a) x = 1, y = -1; 9 | else if (a > b) ext_gcd(a % b, b, x, y), y += a / b * x; 10 | else ext_gcd(a, b % a, x, y), x += b / a * y; 11 | } 12 | long long flsum_t (long long a, long long b, long long c, long long n) { 13 | if (n < 0) return 0; 14 | if (c < 0) a = -a, b = -b, c = -c; 15 | n++; long long res = 0; 16 | if (a < 0 || a >= c) { 17 | long long ra = (a % c + c) % c; 18 | long long k = (a - ra) / c; 19 | res += k * n * (n - 1) / 2; 20 | a = ra; 21 | } 22 | if (b < 0 || b >= c) { 23 | long long rb = (b % c + c) % c; 24 | long long k = (b - rb) / c; 25 | res += k * n; 26 | b = rb; 27 | } 28 | if (a * n + b < c) return res; 29 | else return res + flsum_t(c, (a * n + b) % c, a, (a * n + b) / c - 1); 30 | } 31 | long long flsum (long long a, long long b, long long c, long long st, long long ed) { 32 | return flsum_t(a, b, c, ed) - flsum_t(a, b, c, st - 1); 33 | } 34 | int power(int n, int k, int r) { 35 | int t = n, s = 1; 36 | for (; k; k >>= 1, t = 1LL * t * t % r) 37 | if (k & 1) s = 1LL * s * t % r; 38 | return s; 39 | } 40 | int millerrabin(int x, int tester) { 41 | int k = x-1; for (; !(k & 1); k >>= 1); 42 | int y = power(tester, k, x); 43 | if (y == 1) return 1; 44 | for (; k < x-1; k <<= 1, y = 1LL * y * y % x) 45 | if (y == x-1) return 1; 46 | return 0; 47 | } 48 | int isprime(int x) { 49 | if (x == 2 || x == 7 || x == 61) return 1; 50 | return millerrabin(x, 2) && millerrabin(x, 7) && millerrabin(x, 61); 51 | } 52 | int rho_f(int x, int c, int p) { 53 | return (1LL * x * x + c) % p; 54 | } 55 | int rho(int n) { 56 | int c = rand() % (n-1) + 1, x = 2, y = x, d = 1; 57 | while (d == 1) { 58 | x = rho_f(x, c, n); 59 | y = rho_f(rho_f(y, c, n), c, n); 60 | d = std::__gcd(y > x ? y-x : x-y, n); 61 | } 62 | return d; 63 | } 64 | void factor(int n, std::vector &res) { 65 | if (n == 1) return; 66 | else if (isprime(n)) res.push_back(n); 67 | else if (n == 4) res.push_back(2), res.push_back(2); 68 | else { 69 | int d; 70 | while ((d = rho(n)) == n); 71 | factor(d, res), factor(n / d, res); 72 | } 73 | } 74 | int ind(int a, int b, int m) { 75 | a %= m, b %= m; 76 | std::map hash; 77 | int r = (int)(sqrt(m)), k = 1; 78 | if (r * r < m) r++; 79 | for (int i = 0; i < r; i++, k = 1LL * k * a % m) 80 | if (hash.find(k) == hash.end()) 81 | hash.insert(std::make_pair(k, i)); 82 | int s = 1; 83 | std::map::iterator it; 84 | for (int i = 0; i < r; i++, s = 1LL * s * k % m) { 85 | int x, y, t; 86 | ext_gcd(s, m, x, y); 87 | t = 1LL * b * x % m; 88 | if ((it = hash.find(t)) != hash.end()) 89 | return i * r + it->second; 90 | } 91 | } 92 | void prepare_inv(int *inv, int p) { 93 | inv[1] = 1; 94 | for (int i = 2; i < p; i++) 95 | inv[i] = 1LL * inv[p%i] * (p - p/i) % p; 96 | } 97 | -------------------------------------------------------------------------------- /math/numbertheory/remark.tex: -------------------------------------------------------------------------------- 1 | 数论模板,其中包含: 2 | \begin{itemize} 3 | \item ext\_gcd:扩展欧几里得方法解 $ax+by=\gcd(a,b)$,该函数保证当 $a,b>0$ 时 $x>0$。 4 | \item flsum:欧几里得思想解 $\sum_{x=st}^{ed}\lfloor\frac{ax+b}c\rfloor$。 5 | \item ind:小步大步走算法求 $a^x=m\pmod b$。 6 | \item prepare\_inv:$O(p)$ 求模 $p$ 域下所有非零元的逆元($p$ 素数)。 7 | \end{itemize} 8 | -------------------------------------------------------------------------------- /math/numerical/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | const double pi = 3.14159265358979324; 5 | typedef double (*__F) (double); 6 | double area(double x, double y, double fl, double fmid, double fr) { 7 | return (fl + 4 * fmid + fr) * (y - x) / 6; 8 | } 9 | double area_simpson_solve(__F f, double x, double mid, double y, double fl, double fmid, double fr, double pre, double zero) { 10 | double lmid = (x + mid) / 2, rmid = (mid + y) / 2; 11 | double flmid = f(lmid), frmid = f(rmid); 12 | double al = area(x, mid, fl, flmid, fmid), ar = area(mid, y, fmid, frmid, fr); 13 | if (fabs(al + ar - pre) < zero) return al + ar; 14 | else return area_simpson_solve(f, x, lmid, mid, fl, flmid, fmid, al, zero) + area_simpson_solve(f, mid, rmid, y, fmid, frmid, fr, ar, zero); 15 | } 16 | double area_simpson(__F f, double x, double y, double zero = 1e-10) { 17 | double mid = (x + y) / 2, fl = f(x), fmid = f(mid), fr = f(y); 18 | return area_simpson_solve(f, x, mid, y, fl, fmid, fr, area(x, y, fl, fmid, fr), zero); 19 | } 20 | typedef std::complex complex; 21 | void fft_prepare(int maxn, complex *&e) { 22 | e = new complex[2 * maxn - 1]; 23 | e += maxn - 1; 24 | e[0] = complex(1, 0); 25 | for (int i = 1; i < maxn; i <<= 1) 26 | e[i] = complex(cos(2 * pi * i / maxn), sin(2 * pi * i / maxn)); 27 | for (int i = 3; i < maxn; i++) 28 | if ((i & -i) != i) e[i] = e[i - (i & -i)] * e[i & -i]; 29 | for (int i = 1; i < maxn; i++) e[-i] = e[maxn - i]; 30 | } 31 | /* f = 1: dft; f = -1: idft */ 32 | void dft(complex *a, int N, int f, complex *e, int maxn) { 33 | int d = maxn / N * f; 34 | complex x; 35 | for (int n = N, m; m = n / 2, m >= 1; n = m, d *= 2) 36 | for (int i = 0; i < m; i++) 37 | for (int j = i; j < N; j += n) 38 | x = a[j] - a[j+m], a[j] += a[j+m], a[j+m] = x * e[d * i]; 39 | for (int i = 0, j = 1; j < N - 1; j++) { 40 | for (int k = N / 2; k > (i ^= k); k /= 2); 41 | if (j < i) std::swap(a[i], a[j]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /math/numerical/remark.tex: -------------------------------------------------------------------------------- 1 | 数值计算模板,内含: 2 | \begin{itemize} 3 | \item area\_simpson:Simpson法求定积分。 4 | \item fft\_prepare:求 $maxn$ 次单位根,包括负指数。 5 | \item dft:做长度为 $N(N|maxn)$ 的DFT,其中若 $f=-1$ 则求IDFT。 6 | \end{itemize} 7 | -------------------------------------------------------------------------------- /other/Manacher/main.cpp: -------------------------------------------------------------------------------- 1 | inline int Manacher(int a[], int n, int ret[]) { 2 | int MaxR = -1, where = 0, Ans = 0; 3 | for (int i = 1; i <= n; i++) { 4 | int &it = ret[i]; it = 0; 5 | if (i <= MaxR) it = min(ret[where * 2 - i], MaxR - i); 6 | while (i - it - 1 >= 1 && i + it + 1 <= n && a[i - it - 1] == a[i + it + 1]) it++; 7 | if (it + i > MaxR) MaxR = it + i, where = i; 8 | int tmp = (it << 1) + 1; tmp >>= 1; 9 | if (a[i - it] != '$') tmp++; //如果a[i - it]不是分割符 10 | Ans = max(Ans, tmp); 11 | } 12 | return Ans; 13 | } 14 | -------------------------------------------------------------------------------- /other/Manacher/remark.tex: -------------------------------------------------------------------------------- 1 | Manacher算法求最长的回文半径, 注意原字符串的偶数位置要加占位字符(比如abc变为a$b$c), 返回其最长回文子串的长度。 2 | -------------------------------------------------------------------------------- /other/simplex/main.cpp: -------------------------------------------------------------------------------- 1 | const long double eps = 1e-9; 2 | const int MAXN = 10100; // 最大约束个数 3 | const int MAXM = 1010; // 最大变量个数 4 | const long double inf = 1e100; 5 | 6 | int next[MAXM]; long double a[MAXN][MAXM], b[MAXM]; 7 | int where[MAXM + MAXN]; // where[i] 表示原来第 i 个变量现在在哪个位置 8 | int pos[MAXM + MAXN]; // pos[i] 表示第 i 个位置现在是哪个变量 9 | 10 | int n, m; 11 | 12 | void pivot(int r, int c) { 13 | int &x = pos[r + m], &y = pos[c]; 14 | swap(where[x], where[y]); swap(x, y); 15 | long double t = -a[r][c]; a[r][c] = -1; 16 | int last = MAXM - 1; 17 | for (int i = 0; i <= m; i++) { 18 | a[r][i] /= t; 19 | if (fabs(a[r][i]) > eps) next[last] = i, last = i; 20 | } 21 | next[last] = -1; 22 | for (int i = 0; i <= n; i++) if (i != r && fabs(a[i][c]) > eps) { 23 | t = a[i][c], a[i][c] = 0; 24 | for (int j = next[MAXM - 1]; j != -1; j = next[j]) 25 | a[i][j] += a[r][j] * t; 26 | } 27 | } 28 | 29 | long double get(void) { 30 | long double best, t; int r, c; 31 | for (;;) { 32 | r = c = -1, best = -inf; 33 | for (int i = 1; i <= m; i++) if (a[0][i] > eps) {c = i; break;} 34 | if (c == -1) return a[0][0]; // 从这里返回表示找到了最优解 35 | for (int i = 1; i <= n; i++) if (a[i][c] < -eps && (t = a[i][0] / a[i][c]) > best) best = t, r = i; 36 | if (r == -1) return inf; // 从这里返回表示最优解为inf 37 | pivot(r, c); 38 | } 39 | } 40 | 41 | int init(void) { 42 | for (int i = 1; i <= m + n + 1; i++) where[i] = i, pos[i] = i; 43 | long double best = -eps, r = 0; 44 | for (int i = 1; i <= n; i++) if (a[i][0] < best) best = a[i][0], r = i; 45 | if (!r) return 1; 46 | for (int i = 0; i <= m; i++) b[i] = a[0][i], a[0][i] = 0; a[0][m + 1] = -1; 47 | for (int i = 1; i <= n; i++) a[i][m + 1] = 1; m++; 48 | pivot(r, m); 49 | long double tmp = get(); 50 | if (tmp < -eps) return 0; else { 51 | if (where[m] > m) { 52 | for (int i = 1; i <= m; i++) if (fabs(a[where[m] - m][i]) > eps) { 53 | pivot(where[m] - m, i); 54 | break; 55 | } 56 | } 57 | for (int i = 0; i <= n; i++) a[i][where[m]] = 0; 58 | for (int i = 0; i <= m; i++) a[0][i] = 0; a[0][0] = b[0]; 59 | for (int i = 1; i <= m; i++) if (where[i] > m) { 60 | int t = where[i] - m; 61 | for (int j = 0; j <= m; j++) a[0][j] += b[i] * a[t][j]; 62 | } else a[0][where[i]] += b[i]; 63 | return 1; 64 | } 65 | } 66 | 67 | long double process(void) { 68 | if (!init()) return -inf; 69 | return get(); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /other/simplex/remark.tex: -------------------------------------------------------------------------------- 1 | n个约束,m个变量。 2 | process 返回值为-inf表示无解,inf表示无穷解,否则表示最优解。 3 | 矩阵a[0][1\dots m]表示目标函数的系数。举例线性规划: 4 | $\max(-2x -3y - 4z)$, 其中$3x + 2y + z \le 10, 2x + 5y + 3z \le 15$. 数组a应为: \{\{0,-2,-3,-4\},\{10,-3,-2,-1\},\{15,-2,-5,-3\}\}. 5 | -------------------------------------------------------------------------------- /parse.py: -------------------------------------------------------------------------------- 1 | #-*- encoding: utf-8 -*- 2 | 3 | import os 4 | 5 | remark="remark.tex" 6 | main = "" 7 | 8 | def dfs(level, path): 9 | global main 10 | if os.path.isfile(path + '/' + remark): 11 | i = path + '/' + remark 12 | main += "\\input{%s}\n"%(i) 13 | for i in os.listdir(path): 14 | j = i 15 | i = path + '/' + i 16 | if (os.path.isdir(i) and i != "./.git"): 17 | main += "\\%ssection{%s}"%(level, j) + "\n" 18 | dfs(level + "sub", i) 19 | else: 20 | if (j == remark): continue 21 | else: 22 | if (level != ""): main += "\\code{%s}"%(i) + "\n" 23 | 24 | dfs("", ".") 25 | 26 | print r""" 27 | 28 | \documentclass[a4paper,11pt]{article} 29 | 30 | \usepackage{amsmath} 31 | \usepackage{amssymb} 32 | \usepackage{cmap} 33 | \usepackage{geometry} 34 | \usepackage[bookmarks=true,colorlinks,linkcolor=black]{hyperref} 35 | \usepackage{indentfirst} 36 | \usepackage{xeCJK} 37 | \usepackage{titlesec} 38 | \usepackage{fancyhdr} 39 | 40 | \usepackage{listings} %插入代码 41 | \usepackage{xcolor} %代码高亮 42 | 43 | \pagestyle{fancy} 44 | \fancyhf{} 45 | \rhead{\thepage} 46 | \lhead{Peking University} 47 | 48 | \definecolor{mygreen}{rgb}{0,0.6,0} 49 | \definecolor{AnswerColor}{rgb}{0.8,0,0} 50 | \lstset{ 51 | % xleftmargin=0.5em, xrightmargin=0.5em,aboveskip=1em, 52 | tabsize=4, 53 | basicstyle=\ttfamily\small, 54 | frame=none, 55 | breaklines, 56 | extendedchars=false 57 | keywordstyle=\bfseries\color{black}, 58 | commentstyle=\itshape\color{black}, 59 | language=C++, 60 | } 61 | 62 | \newcommand{\code}[1]{\lstinputlisting{#1}} 63 | 64 | \geometry{margin=1in} 65 | 66 | \setCJKmainfont[BoldFont={Adobe Heiti Std}]{Adobe Song Std} 67 | \setCJKfamilyfont{hei}{Adobe Heiti Std} 68 | %\setmainfont{Times New Roman} 69 | 70 | \title{Obsidian Team Reference} 71 | \author{Peking University} 72 | \date{\today} 73 | 74 | \begin{document} 75 | \maketitle 76 | \tableofcontents 77 | \newpage 78 | """ + main + """ 79 | \end{document} 80 | """ 81 | 82 | --------------------------------------------------------------------------------