其他-一大堆记录 (20 Dec - 08 Nov, 2018)

我好菜啊 Orz

数据结构

平衡树

代码对应 LOJ104 普通平衡树

Scapegoat Tree

#include <cstdio>
#include <ctype.h>
#include <algorithm>

using namespace std;

const int _N = 101000;

char *p1, *p2, buf[1 << 20];

inline char gc()
{
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 20, stdin)) == p1 ? EOF : *p1++;
}

template<typename T>
void rd(T &num)
{
    char tt;
    bool flag;
    while (!isdigit(tt = gc()) && tt != '-');
    if (tt == '-')
        num = 0, flag = 1;
    else
        num = tt - '0', flag = 0;
    while (isdigit(tt = gc()))
        num = num * 10 + tt - '0';
    if (flag)
        num = -num;
    return;
}

namespace goat {

    struct node {
        node *l, *r;
        int v, siz, cnt, exi;
    } GT[_N], *Tl, *Rt, *Lun, *MPL[_N];

    int MPL_cnt, Tmp_cnt, Tmp_v[_N], Tmp_exi[_N];
    double Alpha = 0.6666;

    void init()
    {
        Rt = Tl = Lun = GT;
        Lun->l = Lun->r = Lun;
        Lun->v = Lun->siz = Lun->cnt = Lun->exi = 0;
        MPL_cnt = 0;
        return;
    }

    void mpl_new(node *&p, int x, int exi)
    {
        p = MPL_cnt ? MPL[MPL_cnt--] : ++Tl;
        p->l = p->r = Lun;
        p->v = x;
        p->cnt = 1;
        p->siz = p->exi = exi;
        return;
    }

    void rake(node *&p)
    {
        if (p == Lun)
            return;
        rake(p->l);
        MPL[++MPL_cnt] = p;
        if (p->exi)
            Tmp_v[++Tmp_cnt] = p->v, Tmp_exi[Tmp_cnt] = p->exi;
        rake(p->r);
        return;
    }

    void maint(node *&p)
    {
        p->siz = p->l->siz + p->r->siz + p->exi;
        p->cnt = p->l->cnt + p->r->cnt + 1;
        return;
    }
    
    void build(node *&p, int l, int r)
    {
        if (l > r)
            return;
        int mid = l + r >> 1;
        mpl_new(p, Tmp_v[mid], Tmp_exi[mid]);
        build(p->l, l, mid - 1), build(p->r, mid + 1, r);
        maint(p);
        return;
    }
    
    void ins(node *&p, int x)
    {
        if (p == Lun) {
            mpl_new(p, x, 1);
            return;
        }
        if (p->v == x)
            ++p->exi;
        else if (p->v > x)
            ins(p->l, x);
        else
            ins(p->r, x);
        
        if (p->l->cnt > Alpha * p->cnt || p->r->cnt > Alpha * p->cnt) {
            Tmp_cnt = 0, rake(p);
            build(p, 1, Tmp_cnt);
        }

        maint(p);
        return;
    }

    void del(node *&p, int x)
    {
        if (p == Lun) {
            return;
        }
        if (p->v == x && p->exi)
            --p->exi;
        else if (p->v > x)
            del(p->l, x);
        else
            del(p->r, x);
        maint(p);
        return;
    }

    int get_rank(node *p, int x)
    {
        int t = 1;
        while (p != Lun) {
            if (p->v >= x)
                p = p->l;
            else
                t += p->l->siz + p->exi, p = p->r;
        }
        return t;
    }

    int get_kth(node *p, int x)
    {
        while (p != Lun) {
            if (p->l->siz < x && p->l->siz + p->exi >= x)
                return p->v;
            if (p->l->siz >= x)
                p = p->l;
            else
                x -= p->l->siz + p->exi, p = p->r;
        }
        return -1;
    }

}

using namespace goat;

int N;

int main()
{
    
    rd(N);
    init();
    for (int i = 1; i <= N; ++i) {
        int opt, x;
        rd(opt), rd(x);
        if (opt == 1)
            ins(Rt, x);
        else if (opt == 2)
            del(Rt, x);
        else if (opt == 3)
            printf("%d\n", get_rank(Rt, x));
        else if (opt == 4)
            printf("%d\n", get_kth(Rt, x));
        else if (opt == 5)
            printf("%d\n", get_kth(Rt, get_rank(Rt, x) - 1));
        else if (opt == 6)
            printf("%d\n", get_kth(Rt, get_rank(Rt, x + 1)));
    }
    return 0;
}

Treap

Splay

FHQ Treap

树套树

动态 DP

序列问题(比如区间最大连续和,历史最大值……)往往直接构造状态就可以了,不过用矩阵可能更好想。

树上问题的时候,矩阵才真正发挥作用、虽然有大佬可以观察状态,考虑修改对状态的影响,然后在树剖的线段树上二分最后影响位置,但这样思考太费劲了。直接用矩阵转移就可以了。

这类题代码量特别大,对我这种骨质疏松选手极不友善,所以必须确保头脑清醒再敲代码。

BZOJ4712 洪水

定义这种矩阵乘法:

\[C_{i,j}=min\{ A_{i,k}+B_{k,j}\} \]

发现满足结合律。\(f\) 表示子树最小代价, \(g\) 表示轻儿子的 \(f\) 之和, \(v\) 表示权值。
从儿子 \(x\) 向父亲 \(y\) 转移:

\[ \begin{bmatrix} f(x) & 0\\ \end{bmatrix} \times \begin{bmatrix} g(y) & \infty\\ v(y) & 0\\ \end{bmatrix} = \begin{bmatrix} f(y) & 0\\ \end{bmatrix} \]

树剖,线段树。注意,一些转移的 \(2\times 2\) 矩阵乘起来之后仍然只有两个有意义的元素,所以可以只记录这两个元素。而且这时候会发现,化成最简(大概是那个意思吧)的矩阵转移与一些非常绕的直接 DP 转移做法完全一致。

对于修改操作,只影响重链顶端节点的父亲的(也就是上一个重链底端) \(g\) 。无法直接改,需要计算当前链顶端修改后的增量,然后贡献给父亲。

点分治

  • 找重心,这个过程是 \(O(n\log n)\) 的。

  • 边分治很好,当需要在一堆里面匹配另一堆已有的数据时,因为一共只有两堆。不过边分治需要补成二叉树,否则菊花图就可以卡掉。 如果树并不能随便加点,那就不能边分了。还没有写过。

  • 动态点分治也还没写。

prufer 序列

  • Cayley 公式

  • 带编号、度数有限制的无根树计数。

卷积

  • 多项式乘法原理, \(C_k = \sum A_xB_{k-x}\) 类型的卷积

  • Dirichlet 卷积

\[f(n)\bigotimes g(n)=h(n)\\ h(n) = \sum_{d|n}f(d)\cdot g(n/d)\]

容斥原理

  • 一般的容斥
  • min-max 容斥

矩阵

结合律

找随机的 \(P\) ,乘到两边来验证 \(A \cdot B = C\) 是否成立。为什么有解(即出错)概率是数字范围分之一,我不懂啊!

\((A \cdot B)^n = A \cdot (B \cdot A)^{n-1} \cdot B\) 有时候会有点用。

多次询问求 \(A^n\) 的问题,预处理倍增数组之后可以把每次询问的 \(O(k^3\log n)\) 降到 \(O(k^2\log n)\) ,因为矩阵乘法变成了加法。

行列式

真的不懂啊,不过背下来吧。

多项式

  • 特征多项式
  • 生成函数
posted @ 2018-12-09 15:17  derchg  阅读(179)  评论(0编辑  收藏  举报