分类: OI笔记

  • 数论是坠不吼的

    错排公式

    $$H(n)=(n-1)*(H(n-1)+H(n-2))$$

    $$n!=\sum_{k=0}^{n}*C_n^k*H(k)$$

    二项式反演

    $$F(n)=\sum_{k=0}^nC_n^k*f(k)$$

    $$f(n)=\sum_{k=0}^n(-1)^{n-k}*C_n^k*F(k)$$

    图计数问题

    求n个点的连通图个数.

    $$图的总数-非连通图个数=2^{\frac{n(n-1)}{2}}-不联通图个数$$

    枚举1号点属于的联通块

    求n个点的欧拉图个数(存在一个欧拉回路).
    求n个点的二分图个数.

    中国剩余定理

    x ≡ a1(mod p1)
    x ≡ a2(mod p2)
    x ≡ an(mod pn)

    M=p1*p2*p3…..

    若x为解 x+M与x-M为解

    若x>=0&&x<M则有唯一解

    令Mi=M/pi

    设$$k_i=M_i^{-1}(mod p_i)$$

    $$X=\sum_{i=1}^na_ik_iM_i(mod M)$$

    $$i \ne j时 M_j=0 (mod p )$$

    中国剩余定理的小应mod m_i用
    如果遇到一个模数是合数,而解题需要用到一些模数为质数
    幂次性质的情况时,我们可以把合数拆成若干质数的幂次,然
    后再用CRT合并起来

    BSGS

    求$$A^x=B(mod P)$$

    A,B已知 求[0,P-1]中满足条件的X

    事实上就是一个非常暴力的想法.
    注意到如果设C =sqrt(P)

    ,那么任何一个数都可以写
    成a1 * C + b1的形式,其中a1, b1都< C.
    那么预处理出A_i*C的值.然后在询问时枚举b1,把A
    b1的逆元
    乘一下,再去hash表里查找是否有对应的值即可.
    复杂度为O sqrt(p)

    原根

    听不懂

    二次剩余

     

    听不懂

    勒让德符号

    二次剩余指的是对于a和P,存在x^2 ≡ a(mod P).

    $$(\frac a b )=a^{\frac {p-1} 2}$$

  • 动态规划

    数位dp

    利用前缀和的思想

     

  • 洛谷2221 高速公路

    题目地址

    https://www.luogu.org/problemnew/show/2221

    题目:

    题目描述

    Y901高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。

    Y901高速公路是一条由N-1段路以及N个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站i行驶到i+1(或从i+1行驶到i)需要收取Vi的费用。高速路刚建成时所有的路段都是免费的。

    政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。

    无聊的小A同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的l,r(l<r),在第l个到第r个收费站里等概率随机取出两个不同的收费站a和b,那么从a行驶到b将期望花费多少费用呢?

    输入输出格式

    输入格式:

    第一行2个正整数N,M,表示有N个收费站,M次调整或询问

    接下来M行,每行将出现以下两种形式中的一种

    C l r v 表示将第l个收费站到第r个收费站之间的所有道路的通行费全部增加v

    Q l r 表示对于给定的l,r,要求回答小A的问题

    所有C与Q操作中保证1<=l<r<=N

    输出格式:

    对于每次询问操作回答一行,输出一个既约分数

    若答案为整数a,输出a/1

    输入输出样例

    输入样例#1: 复制

    4 5
    C 1 4 2
    C 1 2 -1
    Q 1 2
    Q 2 4
    Q 1 4
    

    输出样例#1: 复制

    1/1
    8/3
    17/6
    

    说明

    所有C操作中的v的绝对值不超过10000

    在任何时刻任意道路的费用均为不超过10000的非负整数

    所有测试点的详细情况如下表所示

    Test N M

    1    =10    =10
    2    =100    =100
    3    =1000    =1000
    4    =10000    =10000
    5    =50000    =50000
    6    =60000    =60000
    7    =70000    =70000
    8    =80000    =80000
    9    =90000    =90000
    10    =100000    =100000

    这题我周六下午看了整整两节语文课,还好最后想出来了,然后发现就是线段树+一些数学技巧

    这道题目可以抽象为:给定一个数列$$a_n$$,给定数列的一个子数列$$[L,R]$$,求从$$[L,R]$$中等概率的任取两点$$a,b(b>a)$$,$$sum(a,b)$$的期望.

    此外 注意题目有个坑 C l r v是将[l,r]之间的道路的收费加上v

    以及要注意 Q l r 查询的是两座收费站之间的值 等价于查询[l,r-1]之间道路的值

    例如对于数列1 2 2 2(就是样例)

    查询子数列[1,3] 就需要计算($$a_1+(a_1+a_2)+(a_1+a_2+a_3)+(a_2)+(a_2+a_3)+(a_3))/C_{3-1+1}^{2}$$

    观察分子上的值 如果我们将它分成三行

    $$a_1+(a_1+a_2)+(a_1+a_2+a_3)$$

    $$(a_2)+(a_2+a_3)$$

    $$a_3$$

    我们会发现 a1只在第一行出现 出现行数为1 每行出现了3次

    a2在第一行与第二行出现 出现行数为2 每行出现了2次

    a3在三行都出现 出现行数为3 每行出现了1次

    然后我们会发现 区间[l,r]中的每一项在分子上出现的行数+每行出现的次数恰好等于l+r

    emm 至于证明 我也不会

     

    然后我们就有
    分子:$$\sum i {(i-l+1)*(r-i+1)*cost[i]}$$
    分母:$$C_{r-l+1}^{2}=(r-l+1)*(r-l)/2$$
    然后我们将分子进一步拆开可得
    $$\sum {(i-l+1)*(r-i+1)*cost[i]}= (-l*r-l+r+1)*\sum cost[i]+(l+r)*\sum {i*cost[i]}-\sum i {i*i*cost[i]}$$
    这样的话 我们就可以用三棵线段树 分别维护 cost[i] cost[i]*i cost[i]*i*i了
    另外有几个可能会用到的数列求和公式
    $$a_n=n^2$$的前缀和:$$\frac {n(n+1)(2n+1)} 6$$
     

    // luogu-judger-enable-o2
    
    /*
     * 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年11月10日, 下午2:54
     */
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    using std::cin;
    using std::cout;
    using std::endl;
    using std::string;
    using std::max;
    using std::min;
    using std::sort;
    using std::vector;
    using std::map;
    using std::pair;
    using int_t = long long int;
    
    int_t gcd(int_t a, int_t b) {
        if (b == 0) {
            return a;
    
        } else {
            return gcd(b, a % b);
        }
    }
    //数列an=n的前缀和
    
    inline int_t S1(int_t n) {
        return (n * n + n) / 2;
    }
    //数列an=n*n的前缀和
    
    inline int_t S2(int_t n) {
        return n * (n + 1)*(2 * n + 1) / 6;
    }
    
    class SegTree {
    private:
    
        class SegTreeNode {
        public:
            SegTreeNode* left = nullptr;
            SegTreeNode* right = nullptr;
            int_t val = 0;
            int_t mark = 0;
            int_t begin = 0;
            int_t end = 0;
            int_t flag = 0;
    
            SegTreeNode(int_t begin, int_t end) :
            begin(begin), end(end) {
            }
    
            inline int_t length() {
                return end - begin + 1;
            }
    
            virtual ~SegTreeNode() {
                if (left) delete left;
                if (right) delete right;
            }
    
            inline void pushDown() {
                if (mark) {
                    if (left) {
                        left->add(mark);
                    }
                    if (right) {
                        right->add(mark);
                    }
                    mark = 0;
                }
            }
    
            inline void add(int_t x) {
                mark += x;
                if (flag == 0)
                    val += (end - (begin - 1)) * x;
                else if (flag == 1) {
                    val += (S1(end) - S1(begin - 1)) * x;
                } else if (flag == 2) {
                    val += (S2(end) - S2(begin - 1)) * x;
                }
            }
        };
    
        SegTree::SegTreeNode* build(int_t begin, int_t end, int_t* data, int_t flag) {
            SegTree::SegTreeNode* node = new SegTree::SegTreeNode(begin, end);
            node->flag = flag;
            if (begin == end) {
                node->val = data[begin];
                return node;
            }
            int_t mid = (begin + end) / 2;
            node->left = build(begin, mid, data, flag);
            node->right = build(mid + 1, end, data, flag);
            node->val = node->left->val + node->right->val;
            return node;
        }
    
    
    public:
        SegTreeNode* root;
    
        virtual ~SegTree() {
            delete root;
    
        }
    
        SegTree(int_t* data, int_t begin, int_t end, int_t flag) {
            root = build(begin, end, data, flag);
    
        }
    
        int_t query(int_t begin, int_t end, SegTreeNode* curr) {
            //    cout << "querying begin " << begin << " end " << end << " at interval " << curr->begin << " " << curr->end << endl;
            if (begin > curr->end || end < curr->begin) {
                return 0;
            }
            if (begin <= curr->begin && curr->end <= end) {
                return curr->val;
            }
            curr->pushDown();
            return query(begin, end, curr->left) + query(begin, end, curr->right);
        }
    
        int_t modify(int_t begin, int_t end, int_t value, SegTreeNode* curr) {
            int_t result = 0;
            if (begin > curr->end || end < curr->begin) {
                return 0;
            } else if (begin <= curr->begin && curr->end <= end) {
                curr->add(value);
                result = curr->val;
            } else {
                curr->pushDown();
                modify(begin, end, value, curr->left);
                modify(begin, end, value, curr->right);
                result = curr->left->val + curr->right->val;
                
            }
    
            return curr->val=result;
        }
    
    
    };
    
    int_t arr[100000 + 1] = {};
    
    /*
     需要维护
     * cost[i]
     * i*cost[i];
     * i*i*cost[i]
     * 设区间为[l,r] 则费用为
     * (-l*r-l+r+1)*Sigma[i=l->r](cost[i])
     * +(r+l)*Sigma[i=l->r](i*cost[i])
     * -Sigma[i=l->r](i*i*cost[i])
     * 
     * 区间[l,r]所能取到的对数(r-l+1)*(r-l)/2
     */
    int main() {
        int_t n, m;
        cin >> n>>m;
        //维护cost[i]
        SegTree * s1 = new SegTree(arr, 1, n, 0);
        //维护i*cost[i]
        SegTree* s2 = new SegTree(arr, 1, n, 1);
        //维护i*i*cost[i]
        SegTree* s3 = new SegTree(arr, 1, n, 2);
        for (int_t i = 1; i <= m; i++) {
            char opt;
            int_t l, r;
            cin >> opt >> l>>r;
            r--;
            if (opt == 'C') {
                int_t v;
                cin>>v;
                s1->modify(l, r, v, s1->root);
                s2->modify(l, r, v, s2->root);
                s3->modify(l, r, v, s3->root);
            } else if (opt == 'Q') {
                int_t a = (-l * r - l + r + 1) * s1->query(l, r, s1->root)+(r + l) * s2->query(l, r, s2->root) - s3->query(l, r, s3->root);
                int_t b = (r + 1 - l + 1)*(r + 1 - l) / 2;
                int_t x = gcd(a, b);
                a /= x;
                b /= x;
                cout << a << "/" << b << endl;
            }
        }
        return 0;
    
    }
    
    
  • 关于二维ST表

    嗯..这种算法是我一节语文课想出来的qwq

    就是把普通的SparseTable推广到二维的情形。

    在一个矩阵中,预处理出以每一个元素为左上角的大小为$$2^0,2^1,2^2…..$$等的矩阵的最大值或最小值,查询时像普通的ST表那样拼起来就可以了

    注意的是,如果要查询的区域无法使用四个大小相同的正方形覆盖,则需要将查询的区域拆成两个区域分别查询,详情见代码。

    HAOI2007 修筑绿化带

    https://www.luogu.org/problemnew/show/P2219

     

    
    /*
     * 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年11月10日, 下午2:54
     */
    #p\
    r\
    a\
    g\
    m\
    a GCC optimize("O2")
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //#define DEBUG
    
    using std::cin;
    using std::cout;
    using std::endl;
    using std::string;
    using std::max;
    using std::min;
    using std::sort;
    
    using int_t = int;
    const int_t LARGE = 1000;
    int_t mins[LARGE + 1][LARGE + 1][11];
    int_t matrix[LARGE + 1][LARGE + 1];
    
    int_t n, m, a, b, c, d;
    int_t pow2[30];
    
    int_t queryMin(int_t r1, int_t c1, int_t r2, int_t c2) {
        //   cout << "querying min " << r1 << " " << c1 << " " << r2 << " " << c2 << endl;
        int_t rowN = r2 - r1 + 1;
        int_t colN = c2 - c1 + 1;
        //cout << "row n =" << rowN << endl;
        //cout << "col n = " << colN << endl;
        int_t rowK = floor(log2(rowN));
        int_t colK = floor(log2((colN)));
        //cout << " k = " << k << endl;
        if (rowK > colK) {
            int_t mid = (r1 + r2) / 2;
            return min(queryMin(r1, c1, mid, c2), queryMin(mid + 1, c1, r2, c2));
        } else if (colK > rowK) {
            int_t mid = (c1 + c2) / 2;
            return min(queryMin(r1, c1, r2, mid), queryMin(r1, mid + 1, r2, c2));
        } else {
            return min(
                    min(mins[r1][c1][rowK], mins[r1][c2 - pow2[rowK] + 1][rowK]),
                    min(mins[r2 - pow2[rowK] + 1][c1][rowK], mins[r2 - pow2[rowK] + 1][c2 - pow2[rowK] + 1][rowK])
                    );
        }
    }
    
    int_t getSum(int_t r1, int_t c1, int_t r2, int_t c2) {
        return matrix[r2][c2] + matrix[r1 - 1][c1 - 1] - matrix[r1 - 1][c2] - matrix[r2][c1 - 1];
    }
    
    int main() {
        pow2[0] = 1;
        for (int_t i = 1; i <= 29; i++) pow2[i] = pow2[i - 1]*2;
        cin >> n >> m >> a >> b >> c>>d;
        for (int_t i = 1; i <= n; i++) {
            for (int_t j = 1; j <= m; j++) {
                cin >> matrix[i][j];
                matrix[i][j] += -matrix[i - 1][j - 1] + matrix[i][j - 1] + matrix[i - 1][j];
            }
        }
        //枚举出所有的小矩阵
        for (int_t i = 1; i + c - 1 <= n; i++) {
            for (int_t j = 1; j + d - 1 <= m; j++) {
                mins[i][j][0] = getSum(i, j, i + c - 1, j + d - 1);
            }
        }
        for (int_t k = 1; pow2[k] <= min(n - 1, m - 1); k++) {
            for (int_t row = 1; row + pow2[k] + c - 1 <= n; row++) {
                for (int_t col = 1; col + pow2[k] + d - 1 <= m; col++) {
                    mins[row][col][k] = min(
                            min(mins[row][col][k - 1], mins[row + pow2[k - 1]][col][k - 1]),
                            min(mins[row][col + pow2[k - 1]][k - 1], mins[row + pow2[k - 1]][col + pow2[k - 1]][k - 1])
                            );
                }
            }
        }
        int_t result = 0;
        for (int_t i = 1; i + a - 1 <= n; i++) {
            for (int_t j = 1; j + b - 1 <= m; j++) {
                int_t x = queryMin(i + 1, j + 1, i + a - 1 - 1 - (c) + 1, j + b - 1 - 1 - (d) + 1);
                result = max(result, getSum(i, j, i + a - 1, j + b - 1) - x);
            }
    
        }
        cout << result;
        return 0;
    
    }
    
    

    HAOI2007 理想的正方形
    https://www.luogu.org/problemnew/show/2216

    // luogu-judger-enable-o2
    // luogu-judger-enable-o2
    
    /*
     * 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年11月10日, 下午2:54
     */
    #p\
    r\
    a\
    g\
    m\
    a GCC optimize("O2")
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //#define DEBUG
    
    using std::cin;
    using std::cout;
    using std::endl;
    using std::string;
    using std::max;
    using std::min;
    using std::sort;
    
    using int_t = int;
    const int_t LARGE = 1000;
    int_t mins[LARGE + 1][LARGE + 1][11];
    int_t maxs[LARGE + 1][LARGE + 1][11];
    int_t a, b, n;
    int_t pow2[30];
    
    int_t queryMax(int_t r1, int_t c1, int_t r2, int_t c2) {
        int_t rowN = r2 - r1 + 1;
        int_t colN = c2 - c1 + 1;
        int_t k = floor(log2(min(rowN, colN)));
        if (rowN > pow2[k + 1]) {
            int_t mid = (r1 + r2) / 2;
            return max(queryMax(r1, c1, mid, c2), queryMax(mid + 1, c1, r2, c2));
        } else if (colN > pow2[k + 1]) {
            int_t mid = (c1 + c2) / 2;
            return max(queryMax(r1, c1, r2, mid), queryMax(r1, mid + 1, r2, c2));
        } else {
            return max(
                    max(maxs[r1][c1][k], maxs[r1][c2 - pow2[k] + 1][k]),
                    max(maxs[r2 - pow2[k] + 1][c1][k], maxs[r2 - pow2[k] + 1][c2 - pow2[k] + 1][k])
                    );
        }
    }
    
    int_t queryMin(int_t r1, int_t c1, int_t r2, int_t c2) {
        //   cout << "querying min " << r1 << " " << c1 << " " << r2 << " " << c2 << endl;
        int_t rowN = r2 - r1 + 1;
        int_t colN = c2 - c1 + 1;
        //cout << "row n =" << rowN << endl;
        //cout << "col n = " << colN << endl;
        int_t k = floor(log2(min(rowN, colN)));
        //cout << " k = " << k << endl;
        if (rowN > pow2[k + 1]) {
            int_t mid = (r1 + r2) / 2;
            return min(queryMin(r1, c1, mid, c2), queryMin(mid + 1, c1, r2, c2));
        } else if (colN > pow2[k + 1]) {
            int_t mid = (c1 + c2) / 2;
            return min(queryMin(r1, c1, r2, mid), queryMin(r1, mid + 1, r2, c2));
        } else {
            return min(
                    //         cout << " " << mins[r1][c1][k] << " " << mins[r1][c2 - pow2[k] + 1][k] << " " << mins[r1 - pow2[k] + 1][c1][k] << " "
                    min(mins[r1][c1][k], mins[r1][c2 - pow2[k] + 1][k]),
                    min(mins[r2 - pow2[k] + 1][c1][k], mins[r2 - pow2[k] + 1][c2 - pow2[k] + 1][k])
                    );
        }
    }
    
    int main() {
        pow2[0] = 1;
        for (int_t i = 1; i <= 29; i++) pow2[i] = pow2[i - 1]*2;
        cin >> a >> b>>n;
        for (int_t i = 1; i <= a; i++) {
            for (int_t j = 1; j <= b; j++) {
                cin >> mins[i][j][0];
                maxs[i][j][0] = mins[i][j][0];
            }
        }
        for (int_t k = 1; pow2[k] <= min(a, b); k++) {
            for (int_t row = 1; row + pow2[k] - 1 <= a; row++) {
                for (int_t col = 1; col + pow2[k] - 1 <= b; col++) {
                    mins[row][col][k] = min(
                            min(mins[row][col][k - 1], mins[row + pow2[k - 1]][col][k - 1]),
                            min(mins[row][col + pow2[k - 1]][k - 1], mins[row + pow2[k - 1]][col + pow2[k - 1]][k - 1])
                            );
                    maxs[row][col][k] = max(
                            max(maxs[row][col][k - 1], maxs[row + pow2[k - 1]][col][k - 1]),
                            max(maxs[row][col + pow2[k - 1]][k - 1], maxs[row + pow2[k - 1]][col + pow2[k - 1]][k - 1])
                            );
                }
            }
        }
        int_t result = 0x7fffffff;
        for (int_t i = 1; i + n - 1 <= a; i++) {
            for (int_t j = 1; j + n - 1 <= b; j++) {
                result = min(result, queryMax(i, j, i + n - 1, j + n - 1) - queryMin(i, j, i + n - 1, j + n - 1));
            }
        }
        cout << result << endl;
        return 0;
    }
    
    
  • splay树基本思路

    对区间[begin,end]进行修改:

    1.先将l-1旋转到根,然后将r+1旋转到根的右子节点,这是root->rightChd->leftChd所代表的子树就包括了[begin,end]的所有元素

    给区间[begin,end]添加一个值:先执行操作1 然后给root->rightChd->leftChd的标记上加上要添加的值,并且把root->rightChd->leftChd的值加上要加的值,然后再每次进行旋转时将某个点的标记下传

    查询:每个点设置一个值,表示以该节点为根的子树的总和,并且在执行旋转时维护

    区间翻转待添加

    区间删除: 执行操作1 然后删掉root->rightChd->leftChd即可

    区间插入:首先把要插入的区间构建成一棵BST 然后把要插入的位置

  • 关于可持久化线段树的一些体会

    这个玩意我搞了一个月 终于在jzw dalao的指引下成功的搞懂了qwq

    说一下几点注意事项

    1.如果写指针版,那么最好使用定位new运算符从预分配的栈空间上分配内存(new 从heap里分配内存比较耗时)

    2.代表一个节点的结构体里 没用的东西尽量别写(可持久化数据结构十分耗内存)

    注意c++的对象内存对齐

    3.查找区间第k小时 设当前节点为node 如果k<=node->leftChd->value 那么就从node->leftChd里查找第k小 直到找到叶子结点

    如果k>node->leftChd->value  就从node->rightChd里查找第k-node->leftChd->value

    代码:https://www.luogu.org/problemnew/show/3834

     

    /*
     * 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年11月10日, 下午2:54
     */
    #p\
    r\
    a\
    g\
    m\
    a GCC optimize("O3")
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    //#define DEBUG
    using namespace std;
    
    using int_t = int;
    const int_t LARGE = 200000;
    const int_t INF = 0x7ffffff;
    
    struct Node {
        int_t begin = 0;
        int_t end = 0;
        int_t value = 0;
        Node* leftChd = nullptr;
        Node* rightChd = nullptr;
    };
    Node* roots[LARGE + 1];
    
    char buff[sizeof (Node)*1000000 * 7];
    const int_t LENGTH = sizeof (buff) / sizeof (Node);
    int_t used = 0;
    
    Node* nextNewNode() {
        if (used < LENGTH) {
            return new (buff + (used++) * sizeof (Node)) Node;
        } else {
            return new Node;
            
        }
    }
    int_t data[LARGE + 1];
    
    struct Num {
        int_t val;
        int_t id;
    } nums[LARGE + 1];
    
    Node* buildTree(int_t left, int_t right) {
        Node* node = nextNewNode();
        node->begin = left;
        node->end = right;
        if (left == right) {
            node->value = 0;
        } else {
            int_t mid = (left + right) / 2;
            node->leftChd = buildTree(left, mid);
            node->rightChd = buildTree(mid + 1, right);
            node->value = node->leftChd->value + node->rightChd->value;
        }
        return node;
    }
    
    void buildNextTree(Node*& node, int_t pos, Node* x) {
        node = nextNewNode();
        node->begin = x->begin;
        node->end = x->end;
        if (node->begin == node->end && node->begin == pos) {
            node->value = x->value + 1;
        } else if (pos >= node->begin && pos <= node->end) {
            buildNextTree(node->leftChd, pos, x->leftChd);
            buildNextTree(node->rightChd, pos, x->rightChd);
            node->value = node->leftChd->value + node->rightChd->value;
        } else {
            node = x;
        }
    }
    
    int_t query(int_t begin, int_t end, int_t k) {
        Node* leftPtr = roots[begin - 1];
        Node* rightPtr = roots[end];
        while (leftPtr->begin != leftPtr->end) {
            int_t leftSize = rightPtr->leftChd->value - leftPtr->leftChd->value;
            if (k <= leftSize) {
                leftPtr = leftPtr->leftChd;
                rightPtr = rightPtr->leftChd;
            } else {
                k -= (rightPtr->leftChd->value - leftPtr->leftChd->value);
                leftPtr = leftPtr->rightChd;
                rightPtr = rightPtr->rightChd;
            }
        }
        return leftPtr->begin;
    }
    int_t orders[LARGE + 1];
    
    int main(int argc, char** argv) {
        ios::sync_with_stdio(false);
        int_t n, m;
        cin >> n>>m;
        for (int_t i = 1; i <= n; i++) {
            cin >> nums[i].val;
            nums[i].id = i;
        }
        sort(&nums[1], &nums[n + 1], [&](Num & x, Num & y)->bool {
            return x.val < y.val;
        });
        for (int_t i = 1; i <= n; i++) orders[nums[i].id] = i;
        roots[0] = buildTree(1, n);
        for (int_t i = 1; i <= n; i++) {
            buildNextTree(roots[i], orders[i], roots[i - 1]);
        }
        for (int_t i = 1; i <= m; i++) {
            int_t left, right, k;
            cin >> left >> right>>k;
            //   cout << "query " << left << " " << right << " " << k << " = " << query(left, right, k) << endl;
            cout << nums[query(left, right, k)].val << endl;
        }
        return 0;
    }
    
  • 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;
    }
    
  • 一组树剖测试数据

    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<