C. 炮火轰炸
Statement
Solution
Code
int n,q; |
D. 数字积木
Statement
给定一棵有 个结点的树,每个结点的权值构成排列
对树上所有连通子图,计算其结点权值集合的 的总和,结果对 取模。 。
Solution
考虑从小到大枚举 值,因为要使得 ,一定让 全部在子图里,这个过程中符合要求的子图的个数一定是单调不增的。
我们以 为根执行树状 dp ,dp[u] 表示以 为根的子树的个数。
从 到 的过程中,新增了一个必须要连进子图的点 ,它与已有的必须连的子图之间形成了一个链,在这个链上做处理去除不合法的子图:
while(!vis[u]){ |
由于这个过程涉及到模意义下的任意除法,我们需要进行零计数,维护当前乘积中为 的因子数量 zero,以及所有非零因子的乘积。这样删除/加入一个因子时不需要逆元除以 0。
Code
|
G.
Statement
给定一个数组 ,求对于所有可能的起点 ,向右遍历并在当前累加和 时不断累加 所能得到的总和,。
Solution
从小到大遍历 ,令 为以 为起点,以 为终点时的答案。
那么每次遍历到一个新的 ,对于所有 使得 ,令 。这个操作需要 FHQ Treap 维护。
FHQ Treap
Treap = Tree + Heap 。
二叉搜索树有一个性质:左子树的所有节点值 < 根节点值 < 右子树的所有节点值。如果我们把所有的 val 存在一棵 BST 里,当我们需要找 的数时,顺着树根找就能轻松把树按值分割。
如果插入的数据是有序的,会退化成一条链表,每次操作的时间复杂度从 退化成 。
为了防止 BST 退化,我们给每个节点额外随机分配一个 rnd。
我们在维护 BST 性质(按 val 排序)的同时,强制要求树的形态必须满足父节点的优先级 > 子节点的优先级(即形成一个堆)。可以证明只要优先级随机分配,树的期望高度就一定是 。
我们以 为分界把树分裂成两部分 和 ,对大于等于 的那部分( )执行区间修改(使用 lazytag),再把 、、 三部分合并回去。
分裂 (Split)
按阈值 ,把一棵树拆成 的 树和 的 树。
自顶向下:
- 若当前节点 :它和左子树全归 。接下来去切它的右子树。
- 若当前节点 :它和右子树全归 。接下来去切它的左子树。
利用传引用 (&),在向下递归的过程中,顺手把断开的树枝接到了正确的父节点上。
合并 (Merge)
前提是 树里的所有值,必须已经 树里的所有值。
自顶向下:
- 比较 和 根节点的
rnd。 - 若 优先级大: 当新根。将 的右子树和 树继续往下合并。
- 若 优先级大: 当新根。将 树和 的左子树继续往下合并。
Code
|