Live2D

Final Review - 返回天空的雨滴

\[\text{Each moment, now night.} \newcommand{\vct}[1]{\boldsymbol{#1}} \]

  省略编号 \(\overline{xy}_{(9+7)}\) 即第 \(x\) 篇 (国赛训练的) sol set 的第 \(y\) 道题, 链接只指到 sol set, 麻烦自己翻一下.

  麻了, 有 \(17\) 篇 sol sets, 强迫症死掉.

  复习是给自己复习的, 但这篇笔记从动机上是给大家写的, 摆烂的时候随便翻翻也行 w.

Motivations

  动机, 大概就是 "为什么想到这样思考" 的原因. 感性所能及的范围其实远于理性, 做一些元思考工作是重要的.

  • 注意树上路径信息能否转化成到根的路径的差分信息, 自由度直接砍掉一个 \(n\) 岂不美哉? [00]
  • 构造题, 尝试构造 "强化操作", 处理 "原操作组合出强化操作" 和 "强化操作完成构造" 两部分. [01]
  • 算合法需要去重, 算非法也需要去重, 但两个问题的去重难度可能是不一样的, 都值得考虑. [08]
  • 对形如 "能迭代到边界" 的对象计数, "能迭代" 是一个动态的限制, 但 "不能迭代" 是一个静态的限制, 可能更好算. [08] [17]
  • \(\sum a_ib_i\overset{?}{=}0\), 有没有可能是在判断向量共线? [09]
  • 随机序列上的询问可能有很简单的支配对, 而且数量级不大, 注意观察. [0A]
  • 最短路询问 "到底要不要建图跑最短路算法" 其实是很关键的问题, 尝试一些归约. [0B]
  • 长剖/重剖本质上是找到 "若有一个儿子满足某个性质, 则必然是这个特殊儿子", 这个 "特殊性质" 可以拿来保证复杂度, 也可能单纯就是题目需要. [0E]
  • 两种对象互相匹配的问题, "\(A\) 去找 \(B\)" 和 "\(B\) 去找 \(A\)" 同样值得尝试, 尽管可能反直觉. [16]
  • 贪心结论可能让元素的动态贡献变为静态, 如果动态贡献没办法维护也可以反过来想想贪心结论. [18]
  • 数轴上双向不碰撞运动, 可以写成序列 DP (是个线性变换), 引入碰撞时间之类的就可以上 DDP. [1A]
  • 近似解, 兄弟! [1B]
  • \(01\) 矩阵, 要求一个 "\(0\) 的一个角方向上不能有 \(1\)" 之类的东西, 最后的合法矩阵可能存在值的轮廓线. [1C] [CF]
  • 操作序列和计数对象建立双射, 然后就能数操作序列了. [1D] [50]
  • 再相信一次出题人的特殊性质吧! [29]
  • 匹配判定记得 Hall, Hall 的时候注意观察能否只做区间判断. [2C] [AB]
  • "在一个集合中允许任意换走几个, 最小化代价", 如果换入的东西很容易贪心 (例如一定用未入选的代价最少的物品), 是否可以直接钦定贪心结果 (一段最优前缀必选)? [31]
  • 多限制计数, 能否在最外层手动构造容斥关系, 内层只对容斥后限制较少的对象计数? [34] [?6]
  • 多变量关系建议在图上讨论. 没有为什么, 有股劲儿. [3F]
  • 将原问题归约到一个不可做/不可优化问题算是好事, 想想归约的哪个步骤 "失去一般性" 就知道解原问题的关键了. [4A]
  • 别用什么狗吧数据结构了! 颜色变化关系先想想并查集! [63]
  • 注意非古典概型, 你的下意识转化可能荒谬绝伦! [99] [?E]
  • 多元集合方案数, 限制元素和之类的整体信息, 不妨想想数位 DP. [A3]
  • \(n\sim 10^2\)? 说不定是关于 \(\sqrt n\) 的非 poly 算法, 根号平衡! [B1]
  • 也不是一定要拿 \(n=k\) 的情况当作 \(f(n)\) 的子问题的, 全局固定的 \(n\) 可能带来更丰富的信息 [D0]
  • 注意目测 LP 的形式, 很多 LP 可以瞪眼法转费用流. [D3] [DA] [DB]
  • 我又不会数学我为什么还要规避天才的想法? Try to use 组合意义! [D6] [E2]
  • 长度折半? 有时候构造相邻两个元素反向合成一个元素能得到更好的子问题. [E7]
  • 已知每个赛季都会考 Lagrange 插值, 而这个赛季还没考过. [?7]
  • 最后的最后, 请记住, 希望, 是比赛中像钻石一样珍贵的东西. [58] [65] [7E] [C7] [C8] [D7] [FB]

Tricks

  技巧, 让 motivation \(\to\) solution 这段路不完全需要自己走.

  • \(k\) 维偏序, 去他妈的 \(\text{polylog}\), 不如用 std::bitset\(\mathcal O(n^2k/\omega)\). 还可以配合四毛子分块做高维偏序上的信息查询. [07]
  • 经典永流传: 树形拓扑限制, 贪心向爹合并. (用的时候不太自信是怎么回事?) [13]
  • 若提供 "集合合法性" 类的交互接口, 可以通过前缀询问求出第一个合法位置. [16] [B7]
  • 有根树点分治, 分治中心到根的高代价信息可以跳上层分治中心合并. [1F]
  • 计算几何, 一些相交判定问题可以通过网格划分减小检查次数, 网格宽度阈值可以倍增. [21]
  • \(a_i=\min\{a_{i-1}+A(i),B(i)\}\) 这种形式的递推可以枚举前面最后一个取常量 \(B(i)\) 的位置转移. [2D] [95]
  • 模拟费用流的某类增广环没办法维护? 直接跑 Hungary 也不是不行! [43]
  • 反向长剖见过没? [45]
  • 平面图最小割 \(\leftrightarrow\) 对偶图最短路. 反过来用可以, 不过有一些需要注意的很重要的细节, 见例题. [56]
  • 如果只关心一些向量的张成空间, 可以考虑做一些线性组合降低维护难度. [58] [6D]
  • Smith 矩阵类似物, 有趣的矩阵构造. [5E]
  • 将大范围属性随机随机映射到小范围属性上, 这样就能跑非 poly 算法了. [5F]
  • GF 代入数值就不是 GF 了, 各种意义上. [62]
  • \(A=B^k\Rightarrow A'B=kB'A\), 别连 \(n^2\) 快速幂都不会. [64]
  • Lagrange 乘子法一般配合二分 \(\lambda\) 控制变量非负性使用. [69]
  • 线段树维护 LGIS 的 trick 当然不只能维护 LGIS, 留意单调性信息. [6E]
  • 超大值域大序列稀疏二分, 可以在二分区间内随机取一个存在在序列中的值. [77]
  • 双堆 slope trick, 维护 \(f(x)\gets f(x)+|x|\) 之类的东西很舒服. [91]
  • 回文的 border 性质很好, 经常结合 border 论 (比如等差数列划分) 使用. [BD]
  • \(\min\quad z=\sum_ub_ux_u+\sum_{\lang u,v\rang}c_{uv}\max\{0,x_v-x_u-w_{uv}\}\). 会不会建费用流? [D3]
  • 若和式 \(f(n)\) 中带有类似 \(\lfloor n/i\rfloor\) 的取整除法, 可以研究 \(f(n)-f(n-1)\), 此时只有 \(n\) 的因子产生差异贡献. [DD]
  • \(S\)\(T^{+\infty}\) 的匹配判定, 直接考虑 \(T^{114514}TT^{+\infty}\) 的中间那个 \(T\) 是否能让匹配位置稳定循环. 能的话就是匹配不上. [E8]
  • 合法区间长度具有单调性的序列划分问题中, 有总共检查区间长度 \(O(n\log n)\) 的算法. [?4]

Conclusions

  普适结论, 你还记得 NOI 2021 做不起 D1T2 的伤痛吗?

  • 置换自复合, 奇环内部元素不变, 偶环裂成两个等大小环. (可能在倍增中有用.) [02]
  • [Monster Hunting Problem] 先按消耗值升序挑战消耗 \(\le\) 回复的怪兽, 再按回复值降序挑战其余怪兽 [07]
  • 二分图中, 最大独立集 \(=\) 最小点覆盖 \(=\) \(n-{}\)最大匹配 \(=\) \(n-{}\)最大流 (最小割). [1C]
  • 竞赛图的缩点后一定是满足每个点都向后面所有点连边的链. [93] [E7]
  • [BEST Theorem] \(C(G)=T_r(G)\prod_{u\in V}(\deg(u)-1)!\). [E0]
  • \(V\)\(\{0,1\}^k\) 的子空间, 设 \(f(x,V)\)\(x\) 丢进去能异或出的最小值, \(g(x,V)\) 为最大值, 那么 \(g(x\oplus y,V)=f(x,V)\oplus g(y,V)\). [E1]

Algorithms

  背诵任务建议在早读时完成. (?

  锐评: 很多东西都被写过很多遍, 但会且仅会在复习板子的时候被写.


  • long double 模乘

  还不会写? 非常量模数时这玩意儿比 __int128 硬算快.

inline LL mul(const LL u, const LL v) {
    return LL(((ULL)u * v - ULL((LD)u / MOD * v) * MOD + MOD) % MOD);
}

  • 杨表

  配合 Dilworth 做上升链剖分之类的东西, 可以贪心地增量构建. 依次枚举上升链, 能插入最后就插, 否则二分出第一个大于插入数的东西, 交换, 继续迭代.


  • Berlekamp-Massey

  核心思想是用以前的误差项进行简单位移来修正当前误差. 不是特别熟悉, 放个完整代码吧.

inline std::vector<int> getRecursion(const std::vector<int>& seq) {
    std::vector<int> res, bst;
    int w = -1, dw = 0;
    rep (i, 0, (int)seq.size() - 1) {
        int cur = 0;
        rep (j, 0, (int)res.size() - 1) {
            addeq(cur, mul(res[j], seq[i - j - 1]));
        }
        if (cur == seq[i]) continue;

        int dlt = sub(seq[i], cur);
        if (!~w) { w = i, dw = dlt, res.resize(i + 1); continue; }

        auto tmp = res;
        if (res.size() < i - w + bst.size()) res.resize(i - w + bst.size());
        int k = mul(dlt, mpow(dw, MOD - 2));
        addeq(res[i - w - 1], k);
        rep (j, 0, (int)bst.size() - 1) subeq(res[i - w + j], mul(k, bst[j]));
        if (-i + (int)tmp.size() < -w + (int)bst.size()) { // ?
            w = i, dw = dlt, bst = tmp;
        }
    }
    for (int& x: res) x = sub(0, x);
    res.insert(res.begin(), 1);
    return res;
}

我也不知道为什么, 有时候删除 // ? 处的判据会得到更好的东西, 或者说不删会得到依托答辩.

  稀疏矩阵消元 (解 \(A\vct x=\vct b\)): 求出 \(\{A^i\vct b\}\) 的线性递推列 \(\{r_0=1,r_1,\cdots,r_m\}\), 则

\[\vct x=-\frac{1}{r_m}\sum_{i=0}^{m-1}r_{m-1-i}\cdot A^i\vct b. \]

求向量的递推列: 随机一个向量 \(\vct w\), 求 \(\vct w\cdot(A^i\vct b)\) 的递推列就行.


  • K-D Tree

  没啥好说. 建议用数组存各维坐标, 循环是好文明.

  注意替罪羊重构的一些实现细节, 可以选择在 insert 前重构 (只有最高的非平衡子树被重构, 方便维护重构后父子关系) 而非回溯时重构 (路径上每个非平衡子树都被重构, 无效操作很多).


  • PAM

  我草完全不会了. 但是连 SAM 都不考相对冷门的 PAM 更不会考. 但感觉有的时候可以拿来替换 Manacher, 后者实在是太不灵活了.

  两棵树, 处理成 node = last = 1, fail[0] = 1, len[1] = -1; 方便后续操作.

  求 fail, 即找到前缀中最长的能增广 \(s_i\) 的 border:

inline int getFail(const int i, int u) {
    while (S[i] != S[i - len[u] - 1]) u = fail[u];
    return u;
}

新增结点的 fail? 从最长 border 再往上找一个位置: fail[u] = ch[getFail(i, fail[p])][c];.

  匹配, 注意也需要跳 border, 不是所有前缀都有对应状态: u = PAM::ch[PAM::getFail(i, u)][S[i] - 'a'];.

  关于 PAM, 还有个回文划分的 trick. 好长啊, 去 oi-wiki 看吧.


  • Dinic + 势能 Dijkstra

  dis[v] == dis[u] - hgt[v] + hgt[u] + graph[i].cst.


  • 全局平衡二叉树

  爱它一次叭! 你看它维护 DDP 单点修改多么简单:

inline void refresh(int u) {
    while (u) {
        contri(u, -1), pushup(u), contri(u, 1);
        u = fa[u];
    }
}
// eg. val[u] = 114514, BST::refresh(u);

  • K 短路

  没啥好说, 注意题目是否允许到达终点后再出去绕一圈.


  • 扩展 Lucas

  两种. 当 \(p^k\) 很大但 \(p\) 很小时, 用类似快速阶乘算法的东西算.

  当 \(p\) 不小 \(p^k\) 不大的时候, 设 \(n!_p=n!/p^{\alpha_p(n!)}\), 那么 \(n!_p=(n/p)!_p\sum_{i=1}^{n}[p\nmid i]i\), 后面这玩意儿有周期, 预处理 \(p^k\) 以内的结果就行.


  • 毛毛虫剖分

  天呐, 你在复习什么? (有一说一这东西真的挺管用的, 特别是不想写类 DDP 的重剖维护的时候.)

  当然, 一般来说能用到的地方也就只有替代掉类 DDP 维护. 给出一个这种语境下的剖分板子:

inline void reorder(const int u) {
    int but = u;
    top[u] = u, slef[u] = dfc + 1;
    for (int v = son[u]; v; v = son[but = v]) {
        ref[dfn[v] = slef[v] = ++dfc] = v;
    }
    for (int v = but; v != fa[u]; v = fa[v]) {
        lef[v] = dfc + 1, top[v] = u;
        for (int w: adj[v]) if (w != son[v]) ref[dfn[w] = ++dfc] = w;
        rig[v] = dfc;
        for (int w: adj[v]) if (w != son[v]) reorder(w);
        srig[v] = dfc;
    }
}

lef[x]..rig[x]\(x\) 身上的虫足, slef[x]..srig[x]\(x\) 的子树 (如果 \(x\) 是重链顶, 则不含 \(x\)), 应该够用了.


  • ZKW 线段树

  一种当你想要卡常时, 容易凭感觉创造出来的非递归线段树.

  但有板子还是写一写吧. 坑点: 如果维护信息没有交换律 (不能永久化标记), 那么统一下放标记的函数应该长成:

inline void pushall(int u) {
    static int stk[20], top = 0;
    while (u >>= 1) stk[++top] = u;
    while (top) pushdn(stk[top--]);
}

  注意 u 一定是叶子, 而我们没必要也不能对叶子下放标记 (会越界).


  • 树状数组二分

  没学过捏. 从左端点开始从大到小倍增长度就行.


  • Miller Rabin

  选取底数 \(b\), 从 \(k=x-1\) 开始计算 \(b^k\bmod x\) 的值 \(r\): 若 \(r\not\equiv\pm1\pmod x\), 则 \(x\) 不是素数; 若 \(2\nmid k\) 或者 \(r\equiv-1\pmod x\), 则认为 \(x\) 是素数; 否则继续迭代. 好看一点的写法是:

inline bool mrTest(const LL x, const LL b) {
    static LL pwr[70];
    LL k = (x - 1) >> __builtin_ctzll(x - 1);
    pwr[*pwr = 1] = mpow(b, k, x);
    while (k != x - 1) {
        pwr[*pwr + 1] = mul(pwr[*pwr], pwr[*pwr], x);
        ++*pwr, k <<= 1;
    }
    per (i, *pwr, 1) {
        if (pwr[i] == x - 1) return true;
        if (pwr[i] != 1) return false;
    }
    return true;
}

  • Pollard Rho

  倍增步长, 每次用 \(x\mapsto x^2+c\) 生成随机数, 积累一定量之后求乘积与待分解数的 \(\gcd\).

inline LL pollardRho(const LL x) {
    LL st = 0, ed = 0;
    for (int len = 1; ; len <<= 1) {
        LL prd = 1;
        rep (stp, 1, len) {
            prd = mul(prd, labs(st - (ed = rho(ed))), x);
            if (!(stp & 127) && gcd(prd, x) > 1) return gcd(prd, x);
        }
        if (gcd(prd, x) > 1) return gcd(prd, x);
        st = ed;
    }
}

  • 扩展 CRT

  从 \(x=k_1a_1+b_1=k_2a_2+b_2\) 开始合一个同余式就行.


  • 类欧几里德

  只考虑非平凡情况. 把 \((ax+b)/c\) 放到求和指标上枚举 (统计每个 \(1\) 的贡献, 或者对更复杂的贡献形式做差分), 枚举之后把类似 \(\sum_x[x>\cdot]\) 摆弄成 \(\sum_x 1\)\(\sum_x[x\le\cdot]\), 将后式整理成相同形式的问题就能递归求解.


  • 扩展 BSGS

  \(a^x\equiv b\pmod m\), 不断提取 \(d=\gcd(a,m)\) 得到 \(a/d\cdot a^{x-1}\equiv b/d\pmod{m/d}\) 直到 \(a\perp m\).

And …

  会很 personal.

  人菜, 银线随便, 金线随缘. 虽然文化课很 horrible, 但从结果上来说, 假设我需要回去整文化课, 上强基线没有任何问题 (手动去掉这句话的 flag 属性), 况且我拿过十万个 P 的营奖, 也就是说从我所能窥见的事实来预测, 银牌 \(\to\) 高考 \(\to\) P 像是一个很平凡的出路了哈, 真是奇怪.

  那我这百日冲刺在冲什么呢? 比这个出路好的出路, 其实也只有那一条吧, 我说: "爷要进集".

  哈哈哈哈, 其实直到 pjudge day 1 考完, 我都一直在担忧这事儿, 然后用出色的成绩证明了: 我可是 UNR 次强铜牌选手. 后来看到 401rk8 的博客, "对啊, 我还在这儿瞎嚷嚷着进集干嘛?"

  — 那是鞭策而已. 就像一道题站在我面前, 我说 "我要做出它", 但当我的时间所剩无几, 我只能用全力去骗每一档分. 我只需要 score (vi), 然后就不需要担心后路 — 银色的出路很平凡. 我想要 score high, 我想要了却一个没有崇高理由的心愿. Just score higher, and higher, to my limit that I can't tell exactly where it is. 策略开题再说, 目标也懒得再提. 最后一支舞蹈, 享受就好, 享受这烟花燃起后的众生痴迷吧.

posted @ 2023-07-12 23:05  Rainybunny  阅读(329)  评论(3编辑  收藏  举报