杭州集训记

杭州集训记

信友队

5.28

省流:与 T1 鏖战 5 个小时(1 小时打表找规律 + 2 小时写代码 + 1 小时调试 + 1 小时改进速度),写了一个时间复杂度不正确,代码难度巨大,常数巨高的分块做法,本地跑大样例要 40+s(时限 3s),但是居然过了😰

T2 和 T3 赛时没时间看。

100 + 0 + 0,rk 1/3。

A. 长门有希的序列

改自 P4681 [THUSC 2015] 平方运算

C. 小D砍树

改自 CodeChef Chef Cuts Tree。仅有模数不同。

牛逼题。先看一道思路类似的题:

P10632 Normal 给定一棵树,有变量 \(x\),初始为 \(0\)。重复以下过程:

  1. \(x\) 加上树的大小;
  2. 如果树不止一个点,从树上等概率随机选择一个点,删去它,递归进入剩下的子树。

\(x\) 的期望。对 \(998244353\) 取模。

这个过程实际上就是在点分治中随机选择点,而不是选择重心。它不好直接刻画,我们第一步要做的是转化贡献,也就是考虑对于某个特定的选点过程,重写 \(x\) 的式子。

考虑这个过程产生的点分树,我们宣称:

\[x = \sum_{u, v} [u \ \text{is the ancestor of} \ v] \]

也就是如果 \(u\)\(v\) 在点分树上有祖先关系,则产生 \(1\) 的贡献。这是容易理解的:选点选到 \(u\) 时,点分树中 \(u\) 的子树中每个点都产生 \(1\) 的贡献,转化一下就得到了上式。

又注意到点分树中 \(u\)\(v\) 的祖先,当且仅当原图 \(u\)\(v\) 的链中 \(u\) 是最早被选的点。设 \(d(u, v)\) 表示 \(u\)\(v\) 简单路径上的点数,则

\[P(u \ \text{is the ancestor of} \ v) = \dfrac{1}{d(u, v)} \]

据此可以导出 \(O(n^2)\) 的做法。

考虑优化。肯定不能枚举 \(u, v\),注意到我们只关心一个点对的距离,所以考虑对距离为 \(d\) 的点对计数(\(d = 1, 2, \cdots, n\))。设 \(cnt_{d}\) 表示长度为 \(d\) 的链数量,则

\[ans = n + 2\sum_{d = 2}^{n} \dfrac{cnt_{d}}{d} \]

(长度 \(> 1\) 的链两端都可能作为祖先,统计时记得乘 \(2\))。

那么问题的关键在于求出 \(cnt\)。统计树上路径,肯定要想到点分治。如果 \(d\) 是某个定值,那就是点分治板子。这里用卷积来进行多个 \(d\) 的计数。

设当前的分治中心为 \(u\),考虑求出经过 \(u\) 的长度为 \(1, 2, \cdots, n\) 的链数。用类似树上背包的过程,每次新加入一棵子树时,统计某一端在这棵子树内的链的数量,然后把这棵子树合并到之前的连通块上。设当前计算到第 \(i\) 棵子树,前 \((i - 1)\) 棵子树中的链数为 \(f_{1..n}\),以 \(u\) 为某一端的链数为 \(g_{1..n}\),第 \(i\) 棵子树中的链数为 \(h_{1..n}\),有转移

\[f_{k} \gets f_{k} + \sum_{i = 1}^{n} g_{i} \cdot h_{k - i} \\ g_{k} \gets g_{k} + h_{k} \]

第一个转移是卷积的形式,用 NTT 优化,第二个直接相加。显然时间复杂度瓶颈在 NTT,仔细分析一下:

\(ht_i\) 表示第 \(i\) 棵子树的高度。为了优化,\(h\) 的长度只用设为 \(ht_i\),而 \(f\)\(g\) 的长度不超过前 \(i\) 棵子树的 \(ht\)\(\max\)\(2\)。显然把子节点按 \(ht\) 排序更优。这样每次 NTT 的长度为 \(O(ht_i)\),总时间复杂度为

\[\sum_{i = 1}^{|son(u)|} O(ht_{i} \log ht_{i}) \]

由于 \(ht_{i} \le sz_{i}\),所以也可以写成

\[\sum_{i = 1}^{|son(u)|} O(sz_{i} \log sz_{i}) \]

由于子树 \(sz\) 的和就是 \(sz_{u}\),所以上式不超过 \(O(sz_u \log sz_u)\)。(好吧其实我不太会证明这种带渐进符号的东西。)而每次分治的 \(sz_u\) 之和为 \(O(n \log n)\),所以总时间复杂度为 \(O(n \log^{2} n)\)


然后看模拟赛题。还是考虑把贡献拆到点对上。可以发现一个图的权值为连通的有序点对数量。设 \(d(u, v)\) 表示 \(u\)\(v\) 之间简单路径的边数,则随机删除 \(k\) 条边以后 \(u, v\) 连通的概率为

\[\dfrac{\dbinom{n - 1 - d}{k}}{\dbinom{n - 1}{k}} \]

暴力做是 \(O(n^3)\)。考虑优化,显然还是把相同的 \(d\) 放到一起算,用上一题的套路求出 \(cnt_{d}\),那么

\[ans_{k} = \sum_{d = 1}^{n - 1} cnt_{d} \dfrac{\dbinom{n - 1 - d}{k}}{\dbinom{n - 1}{k}} \]

发现式子中有一项同时有 \(k\)\(d\),不能直接统计。我想了很久代数方法把两个变量分离,但都不可行。实际上还是卷积。先再简化一下式子:

\[ans_{k} = \dfrac{(n - 1 - k)!}{(n - 1)!} \sum_{d = 1}^{n - 1} \dfrac{cnt_{d}(n - 1 - d)!}{(n - 1 - (d + k))!} \]

\(f_{k} = cnt_{k}(n - 1 - k)!\)\(g_{k} = (n - 1 - k)!\),则

\[ans_{k} = \dfrac{(n - 1 - k)!}{(n - 1)!} \sum_{d = 1}^{n - 1} f_{k} g_{d + k} \]

发现这就是差卷积的形式。NTT 即可。

然后这道题的原题模数不能直接 NTT,需要三模 NTT/拆系数 FFT,出题人想报复社会吧!/wx

细节:注意求的是有序点对还是无序点对!实现上求的是无序点对,所以长度 \(> 0\) 的链数记得乘 \(2\)

6.4

第三次模拟赛。难度比以前降低了很多,赛时 100 + 70 + 12 = 182,rk 1/3。

三道题都没找到原题。个人难度评估:绿/蓝/紫。

A. 花园

现有一片 \(n \times m\) 大小的花园,即 \((1,1) \sim (n,m)\),每个位置有一个权值 \(c_{i,j}\)

你有一个装置,当你将它放置在位置 \((x,y)\) 时,它将覆盖花园中所有形如 \((x+a_i,y+b_i)\) 的位置(其中 \(1 \leq i \leq k\)\(a\)\(b\) 都是长度为 \(k\) 的数组)。

现在你想选择花园中的两个位置放置该装置,要求最大化被覆盖的位置的权值和。

\(1 \le n, m \le 100\)\(0 \le k \le 10\)

太简单了。枚举一个位置,另一个位置的最大值用线段树维护。视 \(m = O(n)\),时间复杂度 \(O(n^2k^{2}\log n^2)\)。也有别的做法。

B. *先驱者

给定一张 \(n\) 个节点的由邻接矩阵 \(G\) 描述的边权非负的有向图,点编号为 \(1 \sim n\)

定义 \(d(p, x, y)\)途中 不经过 \(p\) 号点的情况下,从 \(x\) 号点到 \(y\) 号点的最短路径长度。特别地,若不存在这样的路径,定义该值为 \(-1\)。若 \(p = x\)\(p = y\),则该值为无限制情况下的最短路径长度。

你需要求出下式的值:

\[\sum_{p=1}^n \sum_{x=1}^n \sum_{y=1}^n d(p, x, y) \]

对于 \(50\%\) 的数据,\(n \le 50\)。对于 \(100\%\) 的数据,\(n \le 500\)

还有 10 分给链,10 分给树。

50 pts 做法:跑 \(n\) 遍 Floyd,\(O(n^4)\)

赛时我一直想着:可以分别从前往后和从后往前跑 Floyd,这样就能求出任意两点经过某个前缀点集/后缀点集的最短路,查询时可以合并两个点集的最短路。但我想了很久都没有想到快速合并两个点集最短路的做法。(实际上合并两个点集的最短路还是只能跑 Floyd,并且只要用到其中一个点集的最短路,然后枚举另一个点集中的点转移。赛时我想着能否利用另一个点集的最短路来加速合并的过程,但毫无前途地倒闭了。)

正解是 Floyd + 分治。其本质思想就是合并两个点集的最短路。具体而言,在 \(solve(l, r)\) 的过程中,要求出的是不经过点集 \([l, r]\) 的最短路。下一层递归到左边 \([l, mid]\) 时,把当前的最短路合并上 \([mid + 1, r]\) 点集;递归到右边 \([mid + 1, r]\) 时,把当前的最短路合并上 \([l, mid]\) 点集。到某个叶子节点 \([p, p]\) 时,就求出了不经过 \(p\) 的最短路。此时用 \(O(n^2)\) 的时间统计答案即可。

代码实现中,对于线段树的每一层,要开一个 \(O(n^2)\) 的数组暂存 Floyd 数组,用于回溯。

此算法的时间复杂度可用递归式 \(T(n) = 2T(\frac{n}{2}) + O(n^3)\) 表示,使用主定理解得 \(T(n) = O(n^3)\)。空间复杂度 \(O(n^2 \log n)\)

C. 黄金矿工

小 T 在玩黄金矿工。

为了简化问题,我们假设矿工位于数轴原点,初始时有 \(n\) 块金子,且每块金子都位于数轴的正半轴上。第 \(i\) 块金子的坐标为 \(x_i\),价值为 \(v_i\)。保证序列 \(x_i\) 严格递增。获得第 \(i\) 块金子所需的时间为 \(x_i \cdot v_i\)。注意:与原版游戏不同,矿工可以花 \(x_i \cdot v_i\) 的时间直接获得某块金子,而不需要把它前面的金子先清理掉。

小 T 想知道在时限内能获得的金子价值之和最大是多少。但关卡有很多,每次通关后,金子以及时限会发生一些变化。具体来说,共有 \(m\) 次操作,操作有以下两种:

  1. 删除第 \(y\) 块金子,保证该金子之前没被删除过。

  2. 询问在有 \(k\) 个单位时间时,获得金子的价值之和最大是多少。注意:每块金子每次询问只能获得一次,且询问之间独立。也就是说,这次获得的金子在之后的询问中依然会出现。

你能帮小 T 在每个关卡都获得理论最高的价值吗?

\(1 \le n \le k_{\max} < 2 \times 10^6\)\(1 \le x_i \cdot v_i \le k_{\max}\)\(1 \le x_1 < x_2 < \cdots < x_n < k_{\max}\), \(1 < m < 5000\), \(1 \le k \le k_{\max}\)

简记 \(k_{\max}\)\(w\)

省流:有大小不超过 \(w\) 的背包,\(n\) 个物品,大小和价值均不超过 \(w\),且物品的大小两两不同。求背包容量为 \(k\) 时物品价值的最大值。多次询问,动态删除物品。

背包问题如果没有特殊性质,只能做到 \(O(n \cdot w)\)。(把操作倒序,删除物品变成加入物品,不改变总时间复杂度。)所以一定要利用题目中的特殊性质。

题中最犀利的特殊性质是 \(x_i\) 两两不同。那么取一阈值 \(b\),满足 \(x_i \le b\) 的物品不超过 \(b\) 个。对于 \(x_i > b\) 的物品,其价值不超过 \(w / b\)。设 \(v_i = c\),则代价大于 \(c \cdot b\),因此价值同为 \(c\) 的物品最多选 \(\frac{w}{bc}\) 个,而且总是优先选 \(x\) 较小的。对 \(c\)\(1\)\(w / b\) 求和,可知 \(x_i > b\) 的物品中可能被选的物品数为

\[\sum_{c = 1}^{w / b} \dfrac{w}{bc} = \dfrac{w}{b} \sum_{c = 1}^{w / b} \dfrac{1}{c} < \dfrac{w}{b} \sum_{c = 1}^{w} \dfrac{1}{c} = O\left( \dfrac{w \log w}{b} \right) \]

所以有用的物品总数为 \(O\left( b + \dfrac{w \log w}{b} \right)\),取 \(b = \sqrt{w \log w}\)\(O(\sqrt{w \log w})\)

先不考虑修改操作。另取一阈值 \(B\),对于 \(x \le B\) 的物品,设 \(f(i)\) 表示背包容量为 \(i\) 时的最大价值(\(i \le w\)),暴力 dp,由于这些物品的数论不超过 \(B\),因此这部分的时间复杂度为 \(O(w \cdot B)\)。对于 \(x > B\) 的物品,注意到它们的价值比较小。具体地,设选择的物品为 \((x_1, v_1), (x_2, v_2), \cdots, (x_p, v_p)\),那么

\[B(v_1 + v_2 + \cdots + v_p) < x_1v_1 + x_2v_2 + \cdots + x_pv_p \le w \]

所以 \(v_1 + v_2 + \cdots + v_p\) 即价值的总和不超过 \(\frac{w}{B}\)。考虑翻转下标与状态,设 \(g(i)\) 表示获得 \(i\) 价值的所需的最少背包容量 (\(i \le \frac{w}{B}\)),则加入一个物品的时间复杂度为 \(O(\frac{w}{B})\)。如果使用有用的物品 dp,则总时间复杂度为 \(O(w\sqrt{w \log w}B^{-1})\)。为了做到这点,先把 \(x\) 从小到大排序(输入已保证有序),对每种 \(v\) 开一个桶,记录已经 dp 的 \(x = v\) 的物品的 \(x \cdot v\) 的总和,如果达到 \(w\) 则不用再 dp 了,因为我们总是贪心选择 \(x\) 较小的。

对于修改操作,还是先倒序,把删除改成加入。\(x \le b\) 的 dp 如常,对于 \(x > b\) 的 dp,记录 \(x \cdot v\) 总和的优化用不了了,因为不保证 \(x\) 有序。但修改操作比较少,所以不加优化也可以接受。

总时间复杂度为

\[O\left( wB + \dfrac{w}{B}(\sqrt{w \log w} + m) \right) \]

为了方便,视 \(m\)\(\sqrt{w \log w}\) 同级。使用均值不等式平衡,解得当 \(B = \sqrt{\sqrt{w \log w}}\) 时,有最优时间复杂度

\[O(w^{\frac{5}{4}}\log^{\frac{1}{4}}w) \]

6.5

80 + 100 + 40 = 220,rk 1/3。T2 太简单。

T1. 逆序对计算

给定一个长度为 \(n\) 的非负整数序列 \(a\)\(q\) 次询问,每次询问给定非负整数 \(x\),求把序列中的每个数异或 \(x\) 后,序列的逆序对数量。

\(n, q \le 10^{6}\)\(0 \le a_{i}, x < 2^{31}\)

套路性地考虑拆位。两个非负整数 \(x < y\) 等价于存在某一二进制位 \(p\),使得二者最高位到第 \((p + 1)\) 位都相同,而 \(x\) 的第 \(p\) 位为 \(1\)\(y\) 的第 \(p\) 位为 \(0\)。那么查询时可以枚举 \(p\) 统计答案。这需要预处理出:

  • \(f(p, x)\),(\(x \in \{0, 1\}\))表示 \(a\) 中有多少个数对 \((i, j)\),满足 \(i < j\)\(a_i\)\(a_j\) 的最高位到第 \((p + 1)\) 位相同,且 \(a_j\) 的第 \(p\) 位为 \(x\)\(a_i\) 的第 \(p\) 位为 \(x \operatorname{xor} 1\)

最暴力的做法是枚举 \(p\),然后使用 map 统计(map 的下标是最高位到第 \((p + 1)\) 位的值),预处理时间复杂度 \(O(n \log n \log w)\),其中 \(w\) 是值域,可以获得 80 pts。

map 换成 Trie,然后所有位一起统计,时间复杂度变成 \(O(n \log w)\)。我怎么完全没想起用 Trie,烂完了。

T2. 边染色

有一棵 \(n\) 个节点的树,你需要将树的边进行染色,一条边可以染成黑色或者白色。有一个关键点 \(u\),一个染色方案合法当且仅当所有点到 \(u\) 的简单路径上至多有一条黑边。你需要回答 \(u = 1, 2, \cdots, n\) 时的合法染色方案数。模 \(10^9 + 7\)

\(n \le 2 \times 10^{5}\)

真不是普及组难度换根 dp?

T3. *哲学树

给定一棵有 \(n\) 个点的树,每个点 \(u\) 有两个属性 \(w_u\)\(v_u\)。在第 \(t\) 秒,\(u\) 的点权为 \(w_u + t \cdot v_u\)。在树上行走,没经过一条边,时间增加 \(1\) 秒。\(q\) 次询问,每次询问给定正整数 \(s, t, x\),求出第 \(x\) 秒从 \(s\) 出发,沿树上唯一简单路径走到 \(t\),路径上所有正数点权的和。

\(n,q \le 3 \times 10^{5}\)\(1 \le x \le n\)

posted @ 2025-06-06 22:19  DengStar  阅读(58)  评论(1)    收藏  举报