如果写递归FFT或者IDFT 一定要注意在IDFT中不要调用FFT!!
作者: officeyutong
-
AC自动机模板
我在这里卡了一个周了,终于写出来了qwq
当走到一个节点时,一定要沿着失配路径走,一直走到根节点,因为失配路径所经过的节点已定能匹配到!
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ /* * File: main.cpp * Author: Ytong * * Created on 2017年12月3日, 下午7:11 */ #pra\ gma GCC optimize("O3") #include #include #include #include #include #include #include #include -
LifeCraft十周目计划
1.7.10的腐竹请注意该版本的Mek存在液体储罐BUG建议Ban液体储罐(感谢星际要塞的反馈)
gregtech-5.09.311.IC2 exp 2.2.828 http://www.mcbbs.net/forum.php?mod=viewthread&tid=289528&highlight=indu%2Bcraft%2B2
2. GT 5.09.31 http://www.mcbbs.net/forum.php?mod=viewthread&tid=352465&highlight=greg%2Btech
3.AE2u rv3-beta13
http://www.mcbbs.net/forum.php?mod=viewthread&tid=674657&highlight=%E5%BA%94%E7%94%A8%E8%83%BD%E6%BA%90
4.TC4.2.3.5
http://www.mcbbs.net/forum.php?mod=viewthread&tid=569963&highlight=thaum%2Bcraft
M-Thaumcraft-1.7.10-4.2.3.5-SaraFix-a1
5.竹
5.末影接口
6.更多实用设备
-
一组树剖测试数据
14 99999 1 99999999
1 2 3 4 5 6 7 8 9 10 11 12 13 14
1 2
1 3
1 4
2 5
2 6
3 7
4 8
4 9
4 10
6 11
6 12
9 13
13 14节点数 操作数 根节点 取模数
点权
边

-
Edmonds Karp算法模板
#include
#include #include #include using std::cin; using std::cout; using std::endl; using std::string; using std::vector; using std::queue; using std::memset; using std::min; using int_t = long long int; struct Edge { int_t from; int_t to; int_t capacity = 0; int_t flow = 0; Edge(int_t from, int_t to, int_t capacity, int_t flow) : from(from), to(to), capacity(capacity), flow(flow) { } }; vector edges; vector graph[100000 + 1]; void insertEdge(int_t from, int_t to, int_t capacity) { edges.push_back((Edge) { from, to, capacity, 0 }); edges.push_back((Edge) { to, from, 0, 0 }); graph[from].push_back(edges.size() - 2); graph[to].push_back(edges.size() - 1); } int_t n, m, start, target; int_t minFlow[100000 + 1]; int_t path[100000 + 1]; int main() { cin >> n >> m >> start>>target; for (int_t i = 1; i <= m; i++) { int_t from, to, cap; cin >> from >> to>>cap; insertEdge(from, to, cap); } int_t maxFlow = 0; while (true) { //BFS queue qu; qu.push(start); memset(minFlow, 0, sizeof (minFlow)); minFlow[start] = 0x7fffffff; memset(path, 0, sizeof (path)); while (qu.empty() == false) { int_t x = qu.front(); qu.pop(); for (int_t edgeId : graph[x]) { Edge& edge = edges[edgeId]; if (minFlow[edge.to] == 0 && edge.flow < edge.capacity) { minFlow[edge.to] = min(minFlow[x], edge.capacity - edge.flow); qu.push(edge.to); path[edge.to] = edgeId; } } if (minFlow[target]) { break; } } if (minFlow[target] == 0)break; // cout << "一条增广路" << endl; for (int_t v = target; v != start; v = edges[path[v]].from) { edges[path[v]].flow += minFlow[target]; edges[path[v]^1].flow -= minFlow[target]; // cout << v << " <- "; } // cout << start< -
Treap树 模板
Treap是一种非常好写而且易于理解的平衡树,然而效率基本看脸,毕竟用到了随机算法。
期望情况下 插入 删除等操作的复杂度均为$$O(log n)$$
详情见注释
#include
using std::cin; using std::cout; using std::endl; using std::string; using int_t = long long int; //表示treap中的一个节点 struct Node { //该节点的值 int_t value; //该节点的随机优先级,用于将treap组织成一个小根堆 //但堆是完全平衡的 而二叉搜索树显然不容易完全平衡 所以priority仅用来使treap带有堆的性质 //所以treap仍然是二叉搜索树 //某个节点的priority必须必它的子节点的小 int_t priority; //同样值的个数,用于值会重复的情况 int_t count = 1; //以该节点为根节点的子树的节点数(考虑重复值的情况) int_t size = 1; //左子节点 Node* left = nullptr; //右子节点 Node* right = nullptr; //父节点 注意 这个值只会在查询时更新 Node* parent = nullptr; Node(int_t value) : value(value) { priority = rand(); } //重新计算该节点的size值 void maintain() { size = count; if (left) size += left->size; if (right) size += right->size; } //返回当前节点左子节点的size 即比当前节点小的节点的个数 int_t leftSize() { if (left) return left->size; else return 0; } }; Node* root = nullptr; //在treap中搜索一个节点,返回节点的指针,如果没找到就返回nullptr Node* lookup(Node* node, int_t value, Node* from = nullptr) { if (node == nullptr) { return nullptr; } //搜索的过程中顺便设置一下父节点 node->parent = from; if (node->value == value) { return node; } else if (value < node->value) { return lookup(node->left, value, node); } else { return lookup(node->right, value, node); } } //把node的右子节点绕node左旋 void leftRotate(Node*& node) { //把node的右节点存起来 Node* temp = node->right; //然后把node的右节点换成tmp的左节点 node->right = temp->left; //把temp的左节点换成node temp->left = node; //然后重新算一下size值 node->maintain(); temp->maintain(); node = temp; } //把node的左子节点绕node右旋 void rightRotate(Node* & node) { Node* temp = node->left; node->left = temp->right; temp->right = node; node->maintain(); temp->maintain(); node = temp; } //向以node为根节点的子树中插入value 并且维护最小堆 void insert(Node*& node, int_t value) { if (node == nullptr) { //如果node==nullptr就说明应该在这个位置插入节点 node = new Node(value); } else { //向左子树插入 if (value < node->value) { insert(node->left, value); //插入结束后,如果插入的节点破坏了最小堆的性质,就需要旋转一下,把优先级小的节点转到上面 //这样做不会破坏二叉搜索树的性质 if (node->left->priority > node->priority) { rightRotate(node); } } else if (value > node->value) { insert(node->right, value); if (node->right->priority > node->priority) { leftRotate(node); } } else { //特殊情况 如果value已经存在于树中 则直接在计数器+1 node->count++; } } //重新算一下size if (node) node->maintain(); } //从以node为根节点的子树中删除value void remove(Node* & node, int_t value) { //找到了要删除的节点 if (node->value == value) { //特殊情况 如果有多个值相同的节点 那么计数器-1即可 if (node->count > 1) { node->count--; } else if (node->left == nullptr) { //如果当前节点没有左子节点 那么就直接把右子节点接上来 Node* bak = node->right; delete node; node = bak; } else if (node->right == nullptr) { Node* bak = node->left; delete node; node = bak; } else { //如果node有两棵子树,那么就把优先级小的那一棵旋转到node,然后在另一个子树(此时node在这个子树里)删除node //执行完多次旋转后,最后一定会到node只有一棵子树或者没有子树的情况 if (node->left->priority < node->right->priority) { rightRotate(node); remove(node->right, value); } else { leftRotate(node); remove(node->left, value); } } } else { if (value < node->value) { remove(node->left, value); } else { remove(node->right, value); } } if (node) node->maintain(); } //输出先序遍历 void preOrder(Node* node) { if (node == nullptr) return; cout << node->value << endl; preOrder(node->left); preOrder(node->right); } //输出中序遍历 void inOrder(Node* node) { if (node == nullptr) return; inOrder(node->left); cout << node->value << endl; inOrder(node->right); } //输出后序遍历 void postOrder(Node* node) { if (node == nullptr) return; postOrder(node->left); postOrder(node->right); cout << node->value << endl; } //求某个节点的前驱 即比这个节点小的最大的节点 //返回该节点指针 若不存在则返回nullptr //需要保证value一定存在于树中 Node* pre(Node* node, int_t value) { node = lookup(node, value); if (node->left) { //如果node有左子节点 那么node的前驱一定是node的左子节点的最右边的节点 node = node->left; while (node->right) node = node->right; return node; } else { //如果node没有左子节点 则顺着node一直往上找 直到找到node是某个节点的左子节点为止 Node* parent = node->parent; while (parent && node != parent->right) { node = parent; parent = parent->parent; } return parent; } } //求节点的后继 与前驱完全对称 不再赘述 Node* post(Node* node, int_t value) { node = lookup(node, value); if (node->right) { node = node->right; while (node->left) node = node->left; return node; } else { Node* parent = node->parent; while (node && node != parent->left) { node = parent; parent = parent->parent; } return parent; } } //在以node为根节点的子树中查询value的排名 //要求value必须存在于树中 int_t rank(Node* node, int_t value) { if (node == nullptr) return 0; //如果value恰好等于node->value 就说明node的左子树的所有节点恰好比value小 node的右子树的节点恰好比value 大 value的名次就是node左子树的节点数 if (value == node->value) { return node->leftSize(); } else if (value < node->value) { //如果value比node->value小 那么就应该去node的左子树确定value的名次 return rank(node->left, value); } else { //如果value比node大 那么value的名次就是 node左子树的大小+值为node->value的元素个数+value在右子树的排名 return rank(node->right, value) + node->count + node->leftSize(); } } //在以node为根的子树内找第k小的元素 Node* kth(Node* node, int_t k) { if (node == nullptr || k < 1 || k > node->size) return nullptr; //如果k恰好是node的名次 //因为同一个元素可能有多个 所以需要这样写 if (node->leftSize() + 1 <= k && k <= node->leftSize() + node->count) { return node; } else if (k <= node->leftSize()) { return kth(node->left, k); } else { return kth(node->right, k - node->leftSize() - node->count); } } int main() { // freopen("Z:/data.in", "r", stdin); // freopen("Z:/data.out", "w", stdout); int_t n; cin>>n; while (n--) { string opt; cin>>opt; if (opt == "insert" || opt == "1") { int_t x; cin>>x; insert(root, x); } else if (opt == "find") { int_t x; cin>>x; cout << lookup(root, x) << endl; } else if (opt == "remove" || opt == "2") { int_t x; cin>>x; remove(root, x); } else if (opt == "pre") { preOrder(root); } else if (opt == "in") { inOrder(root); } else if (opt == "post") { postOrder(root); } else if (opt == "prev" || opt == "5") { int_t x; cin>>x; insert(root, x); cout << pre(root, x)->value << endl; remove(root, x); } else if (opt == "next" || opt == "6") { int_t x; cin>>x; insert(root, x); cout << post(root, x)->value << endl; remove(root, x); } else if (opt == "rank" || opt == "3") { int_t x; cin>>x; insert(root, x); cout << rank(root, x) + 1 << endl; remove(root, x); } else if (opt == "kth" || opt == "4") { int_t x; cin>>x; Node* node = kth(root, x); if (node) cout << node->value << endl; else cout << 0 << endl; } } } -
KMP字符串匹配算法
KMP字符串匹配算法可以理解为一种经过优化的暴力匹配。
#include
#include #include #include #include #include #include #include #include using namespace std; using int_t = long long int; int_t nxt[1000000 + 1]; void getNext(string pattern) { nxt[0] = -1; int_t k = -1; int_t j = 0; while (j < pattern.length()) { if (k == -1 || pattern[j] == pattern[k]) { if (pattern[++j] == pattern[++k]) { nxt[j] = nxt[k]; } else { nxt[j] = k; } } else { k = nxt[k]; } } } int main() { srand(time(0)); string pattern, source; memset(nxt, 0, sizeof (nxt)); int_t x1, x2; cin >> x1>>x2; for (int_t i = 0; i < x1; i++) { source = source + char(rand() % 5 + 'A'); } for (int_t i = 0; i < x2; i++) { pattern = pattern + char(rand() % 5 + 'A'); } cout << source << endl << pattern << endl; int_t i = 0, j = 0; getNext(pattern); while (i < source.length() && j < (signed long long) pattern.length()) { //cout << "x:i=" << i << " j=" << j << endl; if (j == -1 || source[i] == pattern[j]) { //cout << "i=" << i << " j=" << j << " ok" << endl; i++; j++; } else { // cout << "j=" << j << " to " << nxt[j] << endl; j = nxt[j]; } if (j == pattern.length()) { cout << i - j + 1 << endl; i = i - j + 2; j = 0; } // cout << "y:i=" << i << " j=" << j << endl; /// cout << (i < source.length()) << " " << (j < (signed long long )pattern.length()) << endl; } for (int_t i = 0; i < pattern.length(); i++) cout << nxt[i] << " "; cout << endl; main(); return 0; } -
快速乘法
快速乘法是采取类似于快速幂的方式计算乘法主要用于计算$$a*b % c$$ (当a*b超过long long的范围时)。
假设$$a=3_{10}=(0011)_2$$
$$b=5_{10}=(0101)_2$$
那么$$a*b=0011 \times 0101=0011 \times 1 + 0110 \times 0 + 1100 \times 1$$
代码:
int_t quickMultiply(int_t a, int_t b) { int_t result = 0; while (b) { if (b & 1) result = result + a; a = a << 1; b >>= 1; } return result; } -
Trie树
Trie树是一种用来保存字符串集合的树。
Trie树可在$$O(n)$$的时间内将一个字符串插入该集合中,也可以在$$O(n)$$的时间内确定一个字符串是否在集合中。

这是一颗Trie树(图自https://baike.baidu.com/pic/%E5%AD%97%E5%85%B8%E6%A0%91/9825209/0/9252ae7e96e893610dd7da67?fr=lemma&ct=single#aid=0&pic=9252ae7e96e893610dd7da67)
每i层的节点存储字符串中的第i个字符。
代码实现:
#include
#include #include #include #include #include #include using namespace std; using int_t = unsigned long long int; struct TrieNode { TrieNode* chd[26]; int_t value = 0; TrieNode() { memset(chd, 0, sizeof (chd)); } }; TrieNode* root = new TrieNode; void insert(string str, int_t v) { TrieNode * ptr = root; for (int_t i = 0; i < str.length(); i++) { if (ptr->chd[str[i] - 'a'] == nullptr) { ptr->chd[str[i] - 'a'] = new TrieNode; } ptr = ptr->chd[str[i] - 'a']; } ptr->value = v; } vector vec; void map(TrieNode * v) { if (v->value) { for (char chr : vec) cout << chr; cout << endl; } for (int_t i = 0; i <= 'z' - 'a'; i++) { if (v->chd[i]) { vec.push_back(i + 'a'); map(v->chd[i]); vec.pop_back(); } } } bool query(string str) { TrieNode * ptr = root; for (int_t i = 0; i < str.length(); i++) { if (ptr->chd[str[i] - 'a'] == nullptr) { return false; } ptr = ptr->chd[str[i] - 'a']; } if (ptr->value == 0) return false; return true; } int main() { while (true) { string opt; cin>>opt; if (opt == "exit") break; else if (opt == "insert") { string str; cin>>str; insert(str, 1); } else if (opt == "query") { string str; cin>>str; cout << query(str) << endl; } else if (opt == "map") { map(root); } } return 0; } -
在线LaTeX编辑器
http://latex.codecogs.com/eqneditor/editor.php