作者: officeyutong

  • FFT注意!!

    如果写递归FFT或者IDFT 一定要注意在IDFT中不要调用FFT!!

  • 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 
    
    #include 
    #include 
    #include 
    #include 
    using int_t = long long int;
    using std::string;
    using std::cout;
    using std::endl;
    using std::strlen;
    using std::cin;
    using std::queue;
    using std::vector;
    using std::ifstream;
    using std::max;
    
    enum MemoryType {
        preAllocation, dynamicAllocation
    };
    
    struct Node {
        Node* children[26];
        int_t id = 0;
        Node* fail = nullptr;
        //标明这个节点是如何分配内存的
        MemoryType type = MemoryType::preAllocation;
    //当前节点的某个子节点是否在构建失配路径前就已经存在
        bool raw[26];
    
        inline Node* &getChild(char chr) {
            return children[chr - 'a'];
        }
    };
    int_t used;
    //预分配一块内存,用来加快程序分配内存的速度
    char memPool[sizeof (Node)*20000 + 1];
    const int_t size = sizeof (memPool) / sizeof (Node);
    Node* root = nullptr;
    char buff[1000000 + 10];
    char patterns[200][100];
    int_t result[200];
    //获取一个新的Node
    Node* nextNewNode() {
        if (used < size) //预分配的内存还有空位的情况下 直接使用定位new在预分配的内存中分一个Node return new(memPool + (used++) * sizeof (Node)) Node; else { //否则就用new在堆里分配内存 Node* x = new Node; memset(x->children, 0, sizeof (x->children));
            memset(x->raw, 1, sizeof (x->raw));
            x->type = MemoryType::dynamicAllocation;
            return x;
        }
    }
    //将字符串插入到Trie
    inline void insert(const char * str, int_t id) {
        int_t length = strlen(str);
        Node* ptr = root;
        for (int_t i = 0; i < length; i++) {
            //    cout << i << endl;
            //    if (i % 10 == 0) cout << i << endl; if (ptr->getChild(str[i]) == nullptr) {
                ptr->getChild(str[i]) = nextNewNode();
            }
            ptr = ptr->getChild(str[i]);
        }
        ptr->id = id;
    
    }
    //构建失配路径
    inline void buildUpFailPtrs() {
        queue<Node* > qu;
        for (char chr = 'a'; chr <= 'z'; chr++) { //根节点所有子节点的失配路径指向根 if (root->getChild(chr)) {
                root->getChild(chr)->fail = root;
                qu.push(root->getChild(chr));
            }
        }
        while (qu.empty() == false) {
            Node* front = qu.front();
            qu.pop();
            for (char chr = 'a'; chr <= 'z'; chr++) { Node* & chd = front->getChild(chr);
                if (chd == nullptr) {
                    //如果一个节点不存在 那么就补上这条路径 可降低匹配函数中的代码量
                    chd = front->fail->getChild(chr);
                    front->raw[chr - 'a'] = false;
                } else {
                    Node* fail = front->fail;
                    while (fail && (fail->getChild(chr) == nullptr)) {
                        fail = fail->fail;
                    }
                    if (fail == nullptr) fail = root;
                    chd->fail = fail->getChild(chr);
                    if (chd->fail == nullptr) chd->fail = root;
                    qu.push(chd);
                }
            }
        }
    }
    
    inline void match(char * str) {
        memset(result, 0, sizeof (result));
        int_t length = strlen(str);
        Node* ptr = root;
        for (int_t i = 0; i < length; i++) {
            //  if (i % 10 == 0) cout << i << endl; ptr = ptr->getChild(str[i]);
            //        cout << "moving to " << ptr << " with str " << str[i] << endl; if (ptr == nullptr) { ptr = root; continue; } Node* temp = ptr; //重要!!!一定要沿着当前节点的失配路径已知往回走,因为这些失配路径上的字符串一定匹配到了!! while (temp) { // if(temp->id==0) break;
                if (temp->id) {
                    //    cout << "found " << patterns[temp->id] << " at pos " << i;
                    //       cout << "result " << temp->id << " from " << result[temp->id] << " to " << result[temp->id] + 1 << endl; result[temp->id]++;
                }
    
                temp = temp->fail;
            }
    
        }
    
    }
    //删掉树
    void remove(Node* r) {
        if (r == nullptr) return;
        for (char chr = 'a'; chr <= 'z'; chr++) { if (r->raw[chr - 'a'])
                remove(r->getChild(chr));
        }
        //只有某个节点是使用堆分配的内存时才能delete
        //定位new不能用delete
        if (r->type == MemoryType::dynamicAllocation)
            delete r;
    }
    
    int main() {
        int_t n;
        while (true) {
            remove(root);
            memset(memPool, 0, sizeof (memPool));
            used = 0;
            cin>>n;
            if (n == 0) break;
    
            root = nextNewNode();
            for (int_t i = 1; i <= n; i++) { cin >> patterns[i];
                insert(patterns[i], i);
            }
            buildUpFailPtrs();
            cin>>buff;
            match(buff);
            int_t maxTime = 0;
            for (int_t i = 1; i <= n; i++) {
                maxTime = max(maxTime, result[i]);
            }
            cout << maxTime << endl;
            for (int_t i = 1; i <= n; i++) {
                if (result[i] == maxTime) {
                    cout << patterns[i] << endl;
                }
            }
        }
        return 0;
    }
    
  • 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