比赛记录(21~30)
21 CSP-S 模拟赛3
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(10\) | \(100\) | \(0\) | \(210\) |
排名:rank \(2\)。
2 题解
T1
考虑到原先距离 \(2\) 的现在变为距离 \(1\),那么记原先两点间距离为 \(D(i,j)\),那么答案其实就是:
那么这其实就牵扯到距离之间奇偶性的问题,考虑到还要计算距离和,采用树形 dp 的方式。考虑到还需要计算任意两点间距离,考虑以每个点为根计算一边答案,于是想到 up and down。
那么记录下四个信息:到自己距离为偶数的距离之和、到自己距离为奇数的距离之和,到自己距离为偶数的点的个数、到自己距离为奇数的点的个数。暴力树形 dp 即可。
转移方程并不难想,这里就不给了。
T2
看到异或值最大,不难想到利用 01-Trie 求解。现在问题在于前两个性质。首先简单的是第一个性质,其实质就是 \(v\le s-x\),我们在 01-Trie 上贪心求解异或最大值的时候判断一下当前数字的值是否在范围内即可。
现在考虑第二个性质,不难发现其实质就是 \(k\mid v\) 且 \(k\mid x\)。后一个条件我们特判即可,至于前一个条件,考虑对于所有的 \(k\) 建立一颗 01-Trie,上面存储 \(k\) 的所有倍数,这样我们在对应的 01-Trie 上求解即可。
如果担心爆空间还可以使用根号分治,对于 \(k\ge 300\) 的部分暴力求 \(k\) 的倍数,剩下的建 01-Trie 求解。
T3
看到要求最小油箱容量满足所有要求,想到二分答案。我们对于每一次行程都二分一次,然后取最大值即可。check 函数直接贪心即可,可以利用二分查找优化一些。
现在的复杂度是 \(O(nm\log V)\) 的,其实比较危险,我们考虑如何优化复杂度。首先,如果当前求出的最大值已经可以满足当前的行程需求就不必再二分了。也就是说,我们二分的次数其实就是前缀最大值的个数。
考虑一个结论:对于一个随机序列,前缀最大值的个数是 \(O(\log n)\) 级别的。因此我们可以使用一种骚操作:对于原先的序列进行 random_shuffle,这样前缀最大值的个数就是 \(O(\log m)\) 级别的,复杂度就降至 \(O(n\log m\log V)\),可以通过。
T4
炒鸡大码力题。
首先我们必须要知道,如果存在方案,一定是每个人按照一定的顺序从起点不停的走向终点。我们要考虑的其实就是走的顺序。
考虑两个囚犯路径会直接产生冲突的情况,发现不好直接进行判断。我们考虑反向判断必须要满足的条件,看条件之间两两是否互不冲突即可。
容易观察到如下性质:
- 如果 \(A\) 的终点在 \(B\) 的路径上,则 \(B\) 一定要先于 \(A\) 走。
- 如果 \(A\) 的起点在 \(B\) 的路径上,则 \(A\) 一定要先于 \(B\) 走。
现在如果 \(A\) 要先于 \(B\) 走,我们就连边 \(A\to B\);如果出现环,则一定不存在合法方案,否则一定存在。这个可以用拓扑排序简单求解。
现在的新问题是,如果暴力连边,那么复杂度是 \(O(n^2)\) 的,显然无法通过。我们考虑连边的实质,对于一个囚犯 \(i\),其路径为 \((s_i,t_i)\),则所有起点在该路径上的囚犯都要向 \(i\) 连边;同理,该囚犯 \(i\) 应该向所有终点在该路径上的囚犯连边。
容易发现条件中起点和终点的条件是分开的,我们建两棵树 \(S,T\),分别存储每个囚犯路径的起点和终点。根据上面的分析,在 \(S\) 树上路径 \((s_i,t_i)\) 的所有点要向 \(i\) 连边,\(i\) 要向 \(T\) 树上路径 \((s_i,t_i)\) 的所有点连边。为了能够转移限制条件,还需要从囚犯 \(i\) 的终点向 \(i\) 连边,\(i\) 向囚犯 \(i\) 的起点连边。
此时我们发现,连出的边在树上都是连续的一段,想到通过线段树优化建图的方式进行区间和单点之间的连边。而此时的区间是在树上的,不难想到利用树链剖分进行连边。
连完边之后就可以跑拓扑排序找环了,复杂度为 \(O(n\log^2 n)\)。剩下的就全是细节了。
3 挂分
- T2 01-Trie 写假,\(100\to 10\)。
22 CSP-S 模拟赛4
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(62\) | \(60\) | \(20\) | \(242\) |
排名:rank \(4\)。
2 题解
T1
我们以 < 开头举例,不难发现其行走的路径是一段左右横跳的路径,即走到左侧第一个 >,掉头走到右侧第一个 <,接着再掉头走到左侧第二个 >……。我们设右边的 < 的位置分别是 \(r_1,r_2,\cdots\),左侧 > 的位置分别是 \(l_1,l_2\cdots\)。那么行走的总距离就是:
利用前缀和求解即可。注意左右端点处理时的细节即可。
T2
通过扩欧我们知道,对于方程 \(\sum\limits_{i=1}^n a_ib_i=z\),当且仅当 \(\gcd\limits_{i=1}^n(a_i)\mid z\) 的时候有解。但是此时我们发现这个方程解出的 \(b_i\) 可能有负数,我们考虑给式子再加上一项 \(kb_{n+1}\),其意义就是取模 \(k\) 之后的结果,那么有解的情况实际上是 \(\gcd(a_1,a_2,\cdots,a_n,k)\mid z\),而所有能生成的 \(z\) 就是前面 \(\gcd\) 的倍数,枚举即可。
T3
首先容易写出 \(O(n^2m^2)\) 的 dp:将矩阵中所有数按照 \(a\) 的大小从小到大排序,接下来设 \(dp(i,j)\) 表示取到点 \((i,j)\) 时能够取到的最大的和。dp 方程很简单,朴素转移即可:
现在考虑如何优化,我们考虑将绝对值拆开,而所谓的曼哈顿距离其实就是拆开后的式子中取最大值。所以新的转移方程如下:
改写一下形式:
此时不难发现,我们只需要分别维护出 \((dp(i',j')-i'-j'),(dp(i',j')+i'-j'),(dp(i',j')-i'+j'),(dp(i',j')+i'+j')\) 的最大值,然后在上面的 dp 式中 \(O(1)\) 转移即可。记录四个最大值直接各用一个变量记录即可,复杂度就可以降至 \(O(nm)\)
T4
考虑先使用单调栈求出每个位置作为最大值的最大的区间,那么这样下来 \(n\) 个区间一定只有包含或者不相交关系,而不可能有相交关系;同时,这些区间可以构成类似于二叉树的结构。设区间 \([l,r]\) 的最大值位于 \(x\),则 \([l,x-1],[x+1,r]\) 就是下面的儿子。
现在让我们考虑求解第一问,这一问相对简单,只需要求出跨过当前区间的 \(x\) 的所有区间的两端点异或和即可。我们考虑使用启发式合并的方式,枚举左右区间中长度较短的区间,计算与另一个区间之间的贡献,设当前枚举到的数是 \(a_i\)。考虑再设 \(f_{x,i}\) 表示区间 \(x\) 中,二进制第 \(i\) 位上为 \(1\) 的数的个数。那么若 \(a_i\) 的第 \(j\) 位上的数是 \(0\),其对答案的贡献就是 \(f_{rs,j}<<j\);\(a_i\) 的第 \(j\) 位上的数是 \(1\) 同理。
现在考虑求解第二问,有了上面启发式合并的思路这一问也不是很难。我们依然枚举 \(a_i\),问题就转化成在另一个区间中有多少个数 \(v\),满足 \(v\oplus a_i>a_x\)。看到异或不难想到 01-Trie,我们对于每一个区间建立一颗 01-Trie,在上面求出答案即可。
做完这些操作之后要将左右区间的信息合并起来,可以采用 01-Trie 合并的方式;为了节省空间,也可以将左区间信息插入到右区间中,然后将右区间信息直接传递给父亲。
注意到由于每次枚举的区间的长度不大于 \(\dfrac 2n\),因此枚举区间时间复杂度是 \(O(n\log n)\),又由于 01-Trie 本身带有 \(O(\log V)\) 的复杂度,总的时间复杂度就是 \(O(n\log n\log V)\)。
23 CSP-S 模拟赛5
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(60\) | \(50\) | \(0\) | \(21\) | \(131\) |
排名:rank \(10\)。
2 题解
题解按照题目难度排序。
T3
Solution 1:
不难发现根本不存在无解的情况,同时我们按照下面方式操作一定可以得到全白:从根节点开始 DFS,遇到黑点就翻转。同时这种操作的步数也一定是最小的。
接下来手玩样例发现,只有当一个点与其父亲颜色不一致时才会产生一次贡献。那么我们遍历所有黑色节点,先加上其儿子的个数,接下来判断它和父亲的颜色是否一致,一致就再加一,否则减一。
复杂度 \(O(\sum m)\)。
Solution 2:
发现 \(\sum m\le 2\times 10^5\),不难想到虚树。
建出虚树后进行 dp,设 \(f(i),g(i)\) 表示将 \(i\) 的子树全部染成白色 / 黑色的方案数,转移方程是朴素的。但是发现如果只考虑虚树上的儿子是错误的,对于全白的子树也要考虑。所以我们再求出 \(siz(i)\) 表示 \(i\) 的儿子个数,利用这个再进行 dp 即可。
方程较多,不过不难得出。
T2
10pts:暴力枚举分割点即可。
another 40pts:
首先写出一个非常暴力的 dp:设 \(dp(i,j)\) 表示前 \(i\) 个数分成 \(j\) 段,那么方程为:
显然该方程是 \(O(n^2k)\) 的,可惜的是只能拿到 \(0\) pts。我们发现 \(p\) 的值很小,所以后面 \(\bmod p\) 后的值只有 \(100\) 种,考虑枚举 \(sum_{i'}\bmod p\) 的值 \(k\),维护出所有满足 \(sum_{i'}\bmod p = k\) 的 \(dp(i',j)\) 的最小值 \(minn(k,j)\)。然后方程就可以转化为:
复杂度为 \(O(nkp)\)。
100pts:
考虑原先的那个 \(O(n^2k)\) 的 dp,首先我们需要发现一条性质:\(dp(i,j)\equiv sum_i\pmod p\)。
那么现在考虑两个转移:\(dp(a,j-1)+(sum_i-sum_a)\bmod p,dp(b,j-1)+(sum_i-sum_b)\bmod p\)。根据上面那一条性质,我们可以得到:
此时如果我们假设 \(dp(a,j-1)<dp(b,j-1)\),那么一定可以得到:
也就是说状态的转移实际上只与 \(dp\) 的大小有关,我们直接动态维护出所有 \(dp(*,j)\) 的最小值然后转移即可。
T4
构造题。
考虑调整法,首先去满足严格递增的条件。我们设一个 \(B\) 序列为 \([1,2,3,\cdots,n]\),求出 \(\sum A_iB_i=S\)。
\(S=0\) 自然直接输出即可,现在考虑 \(S \neq0\) 的情况。
为了在调整的过程中时刻满足严格递增的条件,我们的操作只能有两个:前缀减或后缀加。
首先我们求出 \(A\) 序列的前缀和 \(pre_i\) 以及后缀和 \(nxt_i\)。可以证明,若两个数组中有正,则必有 \(1\);若两个数组中有负,则必有 \(-1\)。
先假设 \(S>0\),则我们需要将和调小。假如我们找到了 \(pre_i\) 中 \(1\) 的位置 \(x\),我们给区间 \([1,x]\) 的所有数减去 \(S\),整体的和就会正好减去 \(S\),于是就得到了和为 \(0\) 的序列。同理,如果找到了 \(nxt_i\) 中为 \(-1\) 的位置 \(y\),给区间 \([y,n]\) 的所有数减去 \(S\),则整体的和正好回减去 \(-(-S)=S\),依然可以得到和为 \(0\) 的序列。
而如果此时找不到 \(pre_i\) 为 \(1\) 的位置或 \(nxt_i\) 为 \(0\) 的位置,则一定无解。
对于 \(S<0\) 的情况同理,不再赘述。
T1
方法很多,这里讲两种。
Solution 1:
首先我们知道这样一个定理:如果两个点集 \(A,B\),其直径的端点分别是 \((a,b),(c,d)\),那么点集 \(A\bigcup B\) 的直径的两个端点一定是 \(a,b,c,d\) 两两组合。
有了这个定理,我们就可以通过合并点集的方式求出直径的端点和长度。同时不难发现,这个点集的合并是任意的,于是我们想到在线段树上进行合并。
我们在线段树上的区间 \([l,r]\) 中维护编号在 \([l,r]\) 的所有黑点构成的点集的直径端点和长度,在 pushup 中暴力合并即可。
这样做复杂度是 \(O(n\log n)\) 再乘上 LCA 的复杂度,因此用树剖的复杂度是 \(O(n\log^2 n)\),而 欧拉序 + RMQ 的复杂度为 \(O(n\log n)\)。值得注意的是由于倍增的常数过大无法通过。
Solution 2:
显然单次查询我们可以利用点分治来求解,思路是记录当前根节点子树中最长链和次长链长度。
现在多次询问,自然可以想到利用点分树求解。
考虑我们要将儿子的信息整合到父亲上,所以维护出集合 \(A(i)\) 表示点分树上 \(i\) 子树中所有节点到点分树上 \(fa_i\) 的距离。那么对于一个节点 \(i\),整合到父亲上的信息应该是 \(A(i)\) 中的最大值。所以对于每一个节点我们还需要维护出一个 \(B(i)\),表示 \(i\) 的所有儿子 \(j\) 的集合 \(A(j)\) 的最大值构成的集合。
现在我们不难发现,求出 \(B(i)\) 中最大值和次大值的和就可以得出经过点 \(i\) 的路径的最大长度。所以我们还要维护一个集合 \(C\) 表示所有 \(B(i)\) 中最大值和次大值的和,于是 \(C\) 中的最大值就是答案。
考虑 \(A(i),B(i),C\) 都要完成那些操作:基本的有求最大值、次大值,为了完成修改操作还要支持插入、删除。似乎可以利用平衡树来实现这些操作。但是这样做的常数可以说是高到爆炸,同时还过于复杂,因此考虑换一种数据结构。
实际上,这些操作都可以使用堆来实现。最大值、次大值、插入都可以简单的维护出来,但是删除似乎并不可做。实际上,我们可以维护出两个堆 \(val,del\),删除的时候我们只需要将删除的出加入 \(del\) 中,查询的时候弹出所有 \(val\) 和 \(del\) 堆顶相同的元素即可做到删除的效果。这样做复杂度是 \(O(n\log^2n)\) 的,不过常数比平衡树小很多,因此可以通过。
24 CSP-S 模拟赛6
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(58\) | \(20\) | \(20\) | \(198\) |
排名:rank \(7\)。
2 题解
T1
首先我们知道,如果区间不包含 \(1\) 那么一定是不好的。那么我们就要让包含 \(1\) 的区间多。
同时,在上述区间中,如果不同时包含 \(2,3\),那么其 \(\text{MEX}\) 一定是 \(2,3\) 中的一个。因此,我们要让同时包含 \(2,3\) 的区间尽可能少。
于是就可以得出一种构造:将 \(1\) 放在最中间,\(2,3\) 放在两端就可以得到最优解。
T2
看到最大值最小,想到二分答案。我们二分最大的距离,然后利用贪心去求解最少放置的人的数量来判断。
具体的,设当前判断距离 \(s\),我们从最深的儿子开始向上跳它的 \(s\) 级祖先,在这里放下一个人,然后将整颗子树删除。重复上述过程,可以证明,得到的人数是最优的。
当然容易发现这样的算法比较难以实现,考虑利用树形 dp 自上向下进行计算,设 \(f(i)\) 表示 \(i\) 到子树内的点的距离最大值,\(g(i)\) 表示 \(i\) 到子树内已放置的人的距离最小值,DFS 计算即可。
T3
题目实际上可以转化为在序列上的问题来分析。考虑处理 \(A_i,B_i\) 之间冲突的基本方式,在 \(A_i,B_i\) 之间连一条边。此时考虑构成的每一个联通块,若其为树,则能选到的颜色就是节点数减一;否则其能选到的颜色就是节点数。我们可以使用并查集来维护这一过程,插入时更新对答案的贡献即可。
现在考虑到树上怎么做,由于遍历完一颗子树要将子树内的颜色信息删去,因此使用可撤销并查集即可解决问题。
T4
考虑设前 \(i\) 位中正整数和位 \(p_i\),负整数和为 \(q_i\)。那么实际上 \(s_i\) 就可以写作 \(\dfrac{p_i}{P}-\dfrac{q_i}{Q}\)。
现在考虑对于两个位置 \(i,j(i<j)\),若 \(s_i<s_j\),可以得到 \(\dfrac{p_i}{P}-\dfrac{q_i}{Q}<\dfrac{p_j}{P}-\dfrac{q_j}{Q}\)。移项后得到:
显然右边就是一个斜率的形式。我们将每一个点 \((p_i,q_i)\) 放到二维平面上,考虑维护一个上凸包,上凸包上斜率是递减的,那么我们就可以二分出一个点 \((x,y)\),使得该点左侧凸包斜率大于 \(\dfrac QP\)、右侧凸包斜率小于 \(\dfrac QP\),那么这个点对应的 \(s_i\) 一定是最优的。
现在考虑如何进行修改操作。容易发现,修改下标为 \(i\) 位置上的值只会影响 \(i\) 及以后的 \(p_i,q_i\);同时也不难发现,他们的 \(p_i,q_i\) 都只是同时加上或减去了同一个数,因此后面点的坐标只会一起平移。
但是这样做每一次修改完都要重构整个凸包,复杂度过高。考虑利用分块,在每一个块内构造凸包。这样我们只需要重构 \(i\) 所在的块的凸包,后面块对应的凸包只是进行了平移,记录偏移量即可。
那么查询就只需要对每一个块内的凸包进行二分,然后取最大值即可。
25 CSP-S 模拟赛7
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(100\) | \(100\) | \(0\) | \(300\) |
排名:rank \(1\)。
2 题解
T1
容易发现 xt 最后一步的决策一定是放在四个角最优,那么 fengwu 的决策一定是尽可能防到靠中间的位置,于是 xt 第一步的决策一定是尽可能将中间的格子占掉。
于是我们从中间开始扩展,每扩一层就将答案加一,输出即可。
T2
首先我们考虑通过枚举答案,然后求出有多少种数列的答案是我们枚举的值,两者相乘并累加就可以求出答案。
下面定义原题中的 \(s_{1,\cdots ,n-1}\) 为 \(s_{2,n}\),首先我们来看两条结论:
- 如果在一个
0的位置上放置一个小于当前枚举值的数,并且后面所有的0上放大于当前枚举值的数、所有1上放小于等于当前枚举值的数(注意必须要有当前枚举值),则该数列答案必为当前枚举值。 - 如果在一个
1的位置上放置一个大于当前枚举值的数,并且后面所有的1上放小于当前枚举值的数、所有0上放大于等于当前枚举值的数(注意必须要有当前枚举值),则该数列答案必为当前枚举值。
这两个结论不难证明,可以自己手玩一下。
通过上面两个结论我们不难发现,只要枚举当前这个位置,根据其对应的 \(s\) 在这里放下大于或小于枚举值的数,那么满足要求的序列个数是可以通过组合数学计算出来的。具体的,设当前枚举的值为 \(i\),该位置 \(j\) 上的 \(s\) 是 0,且后面 0,1 的个数分别为 \(s1_j,s2_j\),则对应的序列数为:
解释一下:
\(\binom{i-1}{s2_j}\):这里表示该位置后面的
1有 \(s2_j\) 个,加上当前这一位总共有 \(s2_j+1\) 个位置要放下小于等于 \(i\) 的数;又由于这里面必须包含 \(i\) 这个数,所以实际能选出来的放到剩下 \(s2_j\) 个位置中的数只有 \(i-1\) 个,方案数就是组合数了。\(\binom{n-i}{s1_j}\):同上。
\(((s2_j+1)!-s2_j!)\):显然后面 \(s2_j+1\) 个数是可以随便乱换的,但是注意到 \(j\) 这个位置本身并不能放 \(i\),否则会导致答案重复,于是要减掉对应的方案数。
\(s1_j!\):显然这一部分是可以乱换的。其实这里可以和前面的组合数构成排列数 \(A\),但是我考场上脑子太抽没想到。
\((j-1)!\):前面的 \(j-1\) 个位置是可以乱换的。
对于当前位置为 1 是同理的。但是此时注意到一个问题:我们每一次枚举到的真正放下 \(i\) 这个数的位置都在枚举位置之后,也就是说 \(1\) 的位置我们并没有考虑,所以需要特判一下位置 \(1\) 的贡献为:
然后累加答案即可。
T3
我超,原!
见 2024.7.24 T1 Solution 1。
T4
首先考虑利用容斥,设 \(f(i)\) 表示至少有 \(i\) 种调料出现次数少于 \(2\) 的方案数,那么答案就是:
现在考虑怎样求出 \(f(i)\),考虑枚举这些调料中出现次数为 \(1\) 的调料个数,首先选出它们的方案数是 \(\binom{i}{j}\)。接下来考虑它们放到那些面中,我们继续枚举这个值 \(k\),那么将这 \(j\) 种调料放到 \(k\) 碗面中的方案数就是 \(j\brace k\)。
然后考虑这 \(k\) 碗面剩下的调料,显然它们剩下的调料乱放也不会发生重复,而剩下调料有 \(n-i\) 种,于是方案就是 \((2^{n-i})^k=2^{(n-i)k}\)。接下来考虑剩下的面,显然剩下的 \(n-i\) 种调料任选会产生 \(2^{n-i}\) 种面,所以方案数为 \(2^{2^{n-i}}\)。
于是最后的答案就是:
显然暴力枚举是 \(O(n^3)\) 的。考虑优化。首先调换枚举顺序:
考虑后面这个式子 \(\sum\limits_{j=k}^i\binom{i}{j}{j\brace k}\)。从组合角度理解,它相当于从 \(i\) 个中选出 \(j\) 个,将它们分成 \(k\) 个集合。那我们不妨在添加一个 \(k+1\) 集合,将剩下的 \(i-j\) 个数放入该集合。但是还有一个问题是,\(i-j\) 可能为 \(0\),我们给它加上 \(1\),显然不影响答案。于是这个式子的值就是将 \(i+1\) 个元素放入 \(k+1\) 个集合的方案数,显然就是 \({i+1}\brace{k+1}\)。
此时式子就可以求了,我们将组合数、斯特林数、\(2\) 的幂全部提前预处理出来,复杂度就可以降为 \(O(n^2)\),可以通过。
26 CSP-S 模拟赛8
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(20\) | \(40\) | \(100\) | \(22\) | \(182\) |
排名:rank \(2\)。
2 题解
题解按照个人主观题目难度排序。
T3
分情况讨论即可。
typ = 0:
考虑向左走的步数一定等于向右走的步数,向上走的步数一定等于向下走的步数。所以实际上我们只需要枚举这四个量中的一个即可。我们枚举向右走的步数 \(i\),那么向上走的步数就是 \(\dfrac n2-i\)。根据排列组合相关知识可得此时合法方案数为:
暴力枚举 \(i\) 并累加答案即可。复杂度 \(O(n)\)。
typ = 1:
发现要求的本质就是任意时刻向右走的步数都不少于向左走的步数,容易发现是卡特兰数的定义,直接输出 \(H(\dfrac n2)\) 即可。
typ = 2:
容易发现此时我们只会在坐标轴上移动,考虑一个 dp。设 \(dp(i,j,0/1)\) 表示走了 \(i\) 步,停留在 \(x\) / \(y\) 轴的 \(j\) 坐标位置的方案数。转移直接朴素转移即可,注意当 \(j=0\) 时需要特判,此时从 \(x,y\) 轴转移均可。
复杂度 \(O(n^2)\)。
typ = 3:
实际上就是将 typ = 0 和 typ = 1 的情况结合一下,仍然考虑枚举向右走的步数 \(i\),只不过此时还加上了任意时刻向右走步数都不能少于向左走步数的限制,那么结合卡特兰数计算一下就行,方案数为:
T2
分两问考虑即可。
t = 0:
该部分较为简单,设 \(f_i\) 表示最后计算出的答案 \(b_i\),那么显然从父亲向儿子转移的时候,儿子子树内所有点的距离都会减一,其余部分都会加一。设 \(S_i\) 表示 \(i\) 子树内所有 \(a_i\) 的和,那么显然一次转移的贡献就是 \(-S_{to}+S_{rt}-S_{to}\)。所以转移方程为:
两次 DFS 分别求出 \(S\) 和 \(f\) 即可。
t = 1:
20 pts:容易发现所有的式子构成了一个 \(n\) 元一次方程组,利用高斯消元直接 \(O(n^3)\) 解出 \(a\) 即可。
100 pts:
考虑 t = 0 情况中的式子,首先移项可以得到:
显然每一条树边都对应一个式子,那我们将它们全部加起来可以得到:
其中 \(k_i\) 是相加后 \(f_i\) 对应的系数,可以利用一次 DFS 求出。现在考虑这个式子,发现后面的 \(\sum\limits_{i=2}^n S_i\) 实际上就是 \(f_1\) (每个节点都会被加上它的深度次)。那么再次移项就可以得到:
得出 \(S_{rt}\) 后我们再将其带回到上面的式子中,就可以直接求出所有 \(S_{to}\) 了。那么自然的利用差分就可以求出最后的 \(a\) 数组了。
T4
考虑使用连续段 dp。为了满足取最大值的要求,我们规定每次插入连续段的数是从小到大插入的。设 \(dp(i,j,k)\) 表示当前插入 \(i\),当前有 \(j\) 个连续段,且当前和为 \(k\) 的方案数。我们分类讨论:
-
当前插入的 \(i\) 构成新的一段。
显然此时没有造成新的贡献,但是连续段个数加一。所以是从 \(dp(i-1,j-1,k)\) 转移到 \(dp(i,j,k)\)。由于上一次有 \(j-1\) 个连续段,那么此时 \(i\) 插入的位置就有 \(j\) 种,所以转移方程为:
\[dp(i,j,k)=dp(i-1,j-1,k)\times j \] -
当前插入的 \(i\) 接在了一个连续段上。
显然此时贡献 \(i\),连续段个数不变。所以是从 \(dp(i-1,j,k-i)\) 转移到 \(dp(i,j,k)\)。由于上一次有 \(j\) 个连续段,每一个连续段有两个端点,所以 \(i\) 插入的位置有 \(2j\) 种,转移方程为:
\[dp(i,j,k)=dp(i-1,j,k-i)\times 2j \] -
当前插入的 \(i\) 将两个连续段接在了一起。
显然此时贡献为 \(2i\),但是连续段个数减一。所以是从 \(dp(i-1,j+1,k-2i)\) 转移到 \(dp(i,j,k)\)。由于上一次有 \(j+1\) 个连续段,所以 \(i\) 插入的位置有 \(j\) 种,转移方程为:
\[dp(i,j,k)=dp(i-1,j+1,k-2i)\times j \]
直接暴力 \(O(n^2k)\) 转移即可。
T1
神仙题。
首先我们需要得出一个关键的 dp 状态:设 \(dp(i,j)\) 表示选了 \(i\) 个数,得出的结果是 \(j\) 的方案数。那么最后能够得出的所有答案的和就是 \(\sum dp(m,i)\times i\),再除以 \(n^m\) 就可以得出期望。
现在的关键问题就在于如何求 \(dp(i,j)\),首先列出一个暴力的转移方程(下文中 \(p\) 为原题中 \(\text{mod}\)):
其中 \(tot_k\) 表示 \(k\) 出现的次数。显然该转移是 \(O(mp^2)\) 的,十分不优。
考虑如何优化,首先不难发现,如果我们设一个数组 \(mat(i,j)\),令 \(mat(i,(i\times j)\bmod p)=tot_j\),那么上面的转移可以改写为:
容易发现,这就是一个矩阵快速幂优化的标准式,采用矩阵快速幂优化即可,复杂度优化至 \(O(p^2\log m)\)。
接下来有两种思路,一个是符合比赛标题的,另一个是不符合比赛标题的。
Solution 1:
考虑使用倍增的思路。设 \(g(i,j)\) 表示选出 \(2^i\) 个数,得出结果为 \(j\) 的方案数。显然有转移方程:
这一部分时间复杂度是 \(O(p^2 \log m)\)。现在倍增完了之后,我们就可以对 \(m\) 进行二进制分解,然后根据最开始的转移方程直接暴力转移出 \(f\) 的值即可。时间复杂度还是 \(O(p^2\log m)\)。
Solution 2:
前置知识:原根相关。
首先,满足同余式 \(a^x\equiv 1\pmod p\) 的最小正整数 \(n\) 存在,称这个 \(n\) 为 \(a\) 模 \(p\) 的阶,记作 \(\delta_{p}(a)=n\)。
然后,若 \(\gcd(g,p)=1\),且 \(\delta_{p}(g)=\varphi(p)\),称 \(g\) 为模 \(p\) 的原根。
原根的判定条件为:若 \(g\) 为模 \(p\) 的原根,那么对于 \(\varphi(p)\) 的所有质因子 \(p_i\),总有 \(g^{\frac{\varphi(p)}{p_i}}\not\equiv 1\pmod p\)。
原根的一个性质是:对于 \(g^1,g^2\cdots,g^{\varphi(p)}\),它们模 \(p\) 的值互不相同。
因此,若 \(g\) 为模 \(p\) 的原根,则对于满足 \(\gcd(a,p)=1\) 的任意整数 \(a\),必定存在一整数 \(r\),满足 \(1\le r\le \varphi(m)\) 且 \(g^r\equiv a\pmod m\),则称 \(r\) 为模 \(p\) 意义下以 \(g\) 为底 \(a\) 的指标,记作 \(\gamma_{p}(a)\)。
首先这道题中 \(p\) 是质数,意味着上面所有的 \(\varphi(p)\) 都是 \(p-1\)。所以上文提到的性质其实就是说 \(g^1,g^2,\cdots g^{p-1}\) 模 \(p\) 的值互不相同。根据原根定义得 \(g^{p-1}\equiv1\equiv g^0\pmod p\),所以说 \(g^0,g^1,\cdots,g^{p-2}\) 模 \(p\) 后包含了所有 \(1\sim p-1\) 的数,也就是包含了所有的 \(a_i\)。
接下来我们记 \(\gamma(a)\) 表示上文中的 \(\gamma_{p}(a)\),由于上面我们将 \(g^{p-1}\) 换成了 \(g_0\),所以规定 \(\gamma(1)=0\)。
现在我们会看刚才的 dp 方程,发现可以将 \(j\) 的含义替换为得出的结果为原根的 \(j\) 次方,那么转移方程就会变成:
其中 \(tot_k\) 表示 \(\gamma(a_i)=k\) 的 \(i\) 的个数。考虑套用同样的方式进行矩阵快速幂优化,式子为:
其中 \(mat(j,(j+k)\bmod p)=tot_k\)。乍一看复杂度并没有优化,但是如果我们仔细分析这个矩阵会发现,每一行出现的元素是循环出现的,也就是说这是一个循环矩阵,如下:
同时我们发现,只有 \(dp(0,0)=1\),因此实际上我们最后求出的是这个矩阵快速幂后第一行的和。我们考虑一下矩阵乘法的标准式子:
现在让我们根据循环矩阵的特性改写一下。首先我们只关注第一行,所以可以将 \(i\) 变成 \(0\):
不难发现,在循环矩阵中,\(A_{i,j}=A_{i-k,(j-k)\bmod (p-1)}\)。因此上式可以继续改写为:
现在我们就可以将它转成一维数组来看了,即:
显然这个式子是可以 \(O(p^2)\) 求解的。那么将它带入矩阵快速幂中,时间复杂度就可以降为 \(O(p^2\log m)\)。
至此,你成功运用了原根这个省选知识,加上循环矩阵快速幂这个科技,完成了一道 S 组的 T1。
27 CSP-S 模拟赛9
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(77\) | \(10\) | \(59\) | \(40\) | \(186\) |
排名:rank \(10\)。
2 题解
T1
首先发现答案只可能是 \(1/2\),因为最大就是 \(\bmod 2\) 得到的答案。接下来就是考虑怎样判断答案是否为 \(1\)。显然当所有数取模一个 \(p\) 的余数相同,即每个数可以写成 \(pk_i+b\)。做差分后发现每个数就是 \(p(k_i-k_{i-1})\)。
那么我们差分后求出所有数的 \(\gcd\),判断是否是 \(1\)。若 \(\gcd\) 为 \(1\),那么答案就是 \(2\);否则 \(\gcd\) 就是上面的 \(p\),答案就是 \(1\)。
注意判断 \(\gcd\) 为 \(0\) 的情况。
T2
70 ~ 75 pts:
考虑设 \(dp(i,j)\) 表示取 \(i\) 个数,和为 \(j\) 的方案数。由于取得数字中有小数,所以似乎并不好转移。但是发现,我们可以枚举 \(i\) 个数中放 \(1\) 的个数 \(k\),让剩下的 \(i-k\) 个数凑出 \(j-k\) 且不能包含 \(1\)。容易发现,如果将这 \(i-k\) 个数全部乘二,那么得到的问题就将是 “用 \(i-k\) 个数凑出 \(2(j-k)\)”,就消除了刚才不能包含 \(1\) 的限制。所以转移就是:
\(O(n^3)\) dp 即可,实际得分 \(70\sim 75\) pts,具体是多少取决于你的常数。
100 pts:
这道题也是一道集合问题,要求 \(n\) 个数的和为 \(k\)。这让我们想到了 2024.7.10 T3 这道题。那么我们就考虑相同的做法。
依然设 \(dp(i,j)\) 表示选了 \(i\) 个数,和为 \(j\) 的方案数。那么实际上我们的操作就只有两种:向集合内加入一个 \(1\)、集合内所有数除以 \(2\)。那么转移方程就是:
直接 \(O(n^2)\) 暴力转移即可。
T3
30 pts:暴力 \(O(nm)\) 枚举题目描述的过程即可。
another 30 pts:考虑仅需计算出所有 \(10^3\) 之内的坐标的答案,暴力模拟这些坐标最后的结果,复杂度 \(O(10^3m)\)。
100 pts:
由上面的 another 30 pts 的部分分,以及 \(x_i\le 10^6\) 可以看出,我们的求解过程应该是基于值域实现的。也就是说,我们要直接求出所有 \(1\le x\le 10^6\) 的 \(x\) 的答案。
考虑这样一个性质:如果某一时刻两个波特的坐标互为相反数,则他们得到的结果也一定互为相反数。因此,当我们将这 \(10^6\) 个波特移动的时候,在原点两侧且绝对值相同的波特实际上只需要计算一个。那我们就可以将区间以原点为分界线分成两半,将长度更小的一半区间映射到长度更大的一半区间上,然后只需要去计算长度更大的一半区间即可。
但是由于此时的波特较多,所以我们可以考虑去移动原点而不移动波特。最后我们需要将之前舍去点的答案计算出来,可以考虑在映射的时候连一条边,这样最后一定会构成森林,根节点一定会有答案。我们对于每一棵树再 DFS 一遍就可以得出所有坐标的答案了。
T4
65 pts:暴力模拟即可,基本上只要是暴力的复杂度都是 \(65\) pts。
80 pts:
考虑正三角形和倒三角形本质上是一致的,因此只需要考虑正三角形。考虑去枚举正三角形的右下顶点,设 \(l(x,y)\) 表示点 \((x,y)\) 向左上延伸的最大长度,\(r(x,y)\) 表示点 \((x,y)\) 向右上延伸的最大长度,\(d(x,y)\) 表示点 \((x,y)\) 向左最远延伸到的坐标。
那么我们去枚举 \((x,y)\),然后去找三角形的左下角 \((x,t)\),显然这个 \(t\) 的范围是 \([\max\{d(x,y),y-l(x,y)\},y-1]\)。同时,需要满足 \(r(x,t)\ge y-t\),这样就能对答案做出 \(1\) 的贡献。
现在观察上面题目的本质。设 \(L=\max\{d(x,y),y-l(x,y)\},R=y-1\),那么实质上就是要求在区间 \([L,R]\) 范围内满足 \(r(x,t)+t\ge y\) 的 \(t\) 的个数。显然可以使用二维树状数组 / 树套树维护,复杂度 \(O(nm\log ^2 n)\)。
100 pts:
上面做法看上去已经很完美了,不过可惜在选用的数据结构是两只 \(\log\)。
考虑这样一件事:对于一个左下角 \((x,y)\),实际上只有坐标在 \([y+1,y+r(x,y)]\) 的右下角可以接收到它的贡献。那我们不妨考虑使用一维的树状数组,每次在 \(y\) 的位置上加一,\(y+r(x,y)\) 的位置上减一,这样在树状数组查询的时候就可以直接查询 \([L,R]\) 的和了。
但是此时会出现一个问题,如果在 \([L,R]\) 中包含了 \(y+r(x,y)\) 却并没有包含 \(y\),那么我们会多减去一个 \(1\)。因此我们不能将两者同时加减,而应该是在遍历到 \(y\) 的时候将 \(y\) 的位置加一,遍历到 \(y+r(x,y)\) 的时候将 \(y\) 的位置减一,这样就可以动态的维护每一个点的贡献。在枚举的时候求出 \([L,R]\) 的贡献并累加答案即可。
28 CSP-S 模拟赛10
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(0\) | \(30\) | \(27\) | \(157\) |
排名:rank \(10\)。
2 题解
T1
考虑枚举 \(b_1\) 放什么,然后就可以得出 \(x\)。接下来遍历每一位看能否凑出 \(x\) 即可。
T2
与 2024.7.6 T3 可以说是一模一样。设 \(dp(i,j,k,0/1/2)\) 表示当前放了 \(i\) 个 R,\(j\) 个 G,\(k\) 个 Y,且当前位置上放的是 R/G/Y 的最少步数。我们考虑使用与那道题相同的计算步数的方式即可,在此不再赘述。
T3
实际上原题中写的是给右边,不过其实都一样。
40 pts:
首先设每个人给右边了 \(c_i\) 个,那么可以知道,如果所有 \(c_i\) 均 \(>0\),那么将它们全部减去 \(\min\{c_i\}\) 一定会得到 \(c_i=0\)。那么我们就可以去枚举第一个满足 \(c_i=0\) 的 \(i\),也就是强制让 \(i\) 之前的都必须要给。
现在我们就可以从 \(i\) 将这个序列断开,断开后的标号应该是 \(i+1,i+2,\cdots,n,1,2,\cdots,i\)。我们重新标为 \(1\sim n\),然后在上面进行 dp。设 \(dp(i,j)\) 表示新标号后第 \(i\) 个人给出 \(j\) 个后,前 \(i\) 个人的乘积之和。那么转移方程就是:
其中 \(typ\) 是枚举下界,当当前枚举位置的原标号在 \(i\) 之前时 \(typ=1\),否则为 \(0\)。
这样暴力转移求解是 \(O(n^2A_i^2)\) 的,只能拿到 \(40\) pts。
不要着急,下面展示如何将 \(O(n^2A_i^2)\) 的算法优化到 \(O(n)\)。
50 pts:
首先将 dp 的式子拆开,可以得到:
容易发现,只需要我们维护出 \(\sum dp(i-1,k)\) 和 \(\sum dp(i-1,k)\times k\),那么后面的转移就是 \(O(1)\) 的了,复杂度降为 \(O(n^2A_i)\)。
80 pts:
我们发现,最后我们要求出的实际上就是新标号后的 \(dp(n,0)\)。那么这个时候我们列出转移方程:
容易发现,实际上我们只需要知道 \(\sum dp(n-1,k)\) 和 \(\sum dp(n-1,k)\times k\) 就可以求出最后答案。再结合上面的 50 pts 的做法,可以发现,我们根本不需要知道 \(dp(i,j)\) 具体的值,只需要知道 \(\sum dp(i,j)\) 和 \(\sum dp(i,j)\times j\) 的值即可。
于是我们就可以列出新的转移式:
前面的系数 \(A,B,C,D\) 是要根据 \(typ\) 来计算的,具体的不再赘述。现在的转移就是正常的 \(O(n^2)\) 了,可以砍下 \(80\) pts。
100 pts:
容易发现上面的两个和式的转移颇有矩阵乘法的味道,那我们就可以将它写成矩阵乘法的形式。现在发现对于每一个枚举的 \(c_i=0\) 的 \(i\),只要将它后面的矩阵相乘,然后再乘上前面的矩阵,就可以得到最后的两个和式。
那么我们将每个位置上对应的矩阵算出来,然后接下来求出前缀积和后缀积,这样就能单次 \(O(1)\) 求出答案。最后我们的复杂度就优化到了 \(O(n)\)。
T4
考虑将每个点向在它前面且离他最近且比他小的点连边,显然会形成一棵树。
可以发现对于一个数 \(x\) 一直到下一个大于等于他的数之前会被他所影响,而这些点均位于 \(x\) 的子树中,那么也就是说每个结点的子树是一个连续区间。
因为在位置是连续的,所以被改变的时间也是连续的,所以可以维护增量。
我们可以对于每一个位置搞一个四元组 \((t_0,l,r,d)\),表示从 \(t_0\) 时刻开始,在区间 \([l,r]\) 内会从左往右每一次带来 \(d\) 的增量。但是这个并不是很好维护,我们利用差分将其改写成三元组,即 \((t_0,l,d)\) 表示从 \(t_0\) 时刻开始,从 \(l\) 依次从左向右带来 \(d\) 的增量。
现在考虑这些三元组对答案的贡献。首先将查询拆成前缀和作差的形式,假如现在要查询前缀 \(p\) 在 \(t\) 时刻的答案,那么贡献就是 \((\min(p,l+t-t_0)-\min(p,l-1))\times d\),也就是 \((t+\min(p-t,l-t_0)-\min(p,l-1))\times d\)。于是我们开四颗线段树,分别维护下列值即可:
- 在 \(l-1\) 位置维护 \(l\times d\)。
- 在 \(l-1\) 位置维护 \(d\)。
- 在 \(l-t_0\) 位置维护 \((l-t_0)\times d\)。
- 在 \(l-t_0\) 位置维护 \(d\)。
29 CSP-S 模拟赛11
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(100\) | \(6\) | \(0\) | \(206\) |
排名:rank \(6\)。
2 题解
T1
枚举化简后的 \(\dfrac pq\) ,然后看有多少数能约分成它即可。
T2
显然行列互不冲突,考虑分开求解。假如 \(i,j\) 可以交换,就在 \(i,j\) 之间连一条边。容易看出,对于三个点 \(i,j,k\),若 \(i\) 可与 \(j\) 交换、\(j\) 可与 \(k\) 交换,则可以做到 \(i\) 与 \(k\) 交换的效果。因此在一个联通块内的所有点可以形成全排列,利用并查集简单维护即可。
T3
首先考虑设 \(g_i\) 为第 \(i\) 根水管维修的概率,那么答案既然就是 \(\sum g_i\times i\)。
容易发现水管维修只可能是前面的水管都被维修,或者前面的水管都不爆。因此该点爆掉的期望只与前面修完后剩下的轮数有关。考虑设 \(dp(i,j)\) 表示前 \(i\) 个水管总共只维修了 \(j\) 次的概率。那么有方程:
然后 \(g_i\) 自然就直接是 \(\sum dp(i-1,j-1)\times(1-(1-p_i)^{r-j+1})\) 了。
T4
第一个操作相当于区间平移,想到区间平衡树。
然后发现可以对每个颜色维护一颗平衡树,查询时只需要查出下标在 \([L,R]\) 范围内的有多少个即可。显然用 FHQ 可以简单实现。
现在问题在于如果每一次修改都要重新维护每种颜色出现的位置复杂度显然爆炸,考虑每个颜色出现的位置和什么有关,显然就是区间平衡树上节点的排名。于是我们在每种颜色的平衡树上可以维护出该颜色出现位置对应区间平衡树上的节点编号,权值就是节点的排名。这样就可以直接 \(O(n\log n)\) 求解了。
3 挂分
- T3 数组开小,\(11\to 6\)。
- T4 未读清题意,\(20\to 0\)。
30 CSP-S 模拟赛12
1 得分
| 题目 | T1 | T2 | T3 | T4 | 总分 |
|---|---|---|---|---|---|
| 得分 | \(100\) | \(100\) | \(60\) | \(0\) | \(260\) |
排名:rank \(5\)。
2 题解
T1
考虑枚举集合的 \(\text{MEX}\) 是什么,那么它前面的所有数就必须要选。设当前枚举的数是 \(x\),那么合法方案数就是 \(C_{n-x-1}^{k-1}\),再除以总方案数就可以得到 \(x\) 出现的概率,最后乘上 \(x\) 就是期望。
T2
考虑拆式子。
如果暴力将整除符号拆开,那么实际上只有当 \(a_i,a_j\) 都为奇数时才会丢失 \(1\) 的贡献。那不妨将它放在一边考虑,于是式子就变成:
\(A\) 就是两者都为奇数时产生的贡献。那么这个时候我们就可以对后面的 \(j\) 进行枚举了,式子变成:
其中 \(B\) 表示后面求和时当 \(m\) 为偶数时多出的 \(\dfrac m2\) 的贡献。现在这个求解已经是 \(O(nm)\) 的了。
显然 \(a_i\) 也可以枚举,那么最后复杂度就可以化成 \(O(n)\) 了。
T3
考虑每个点都有其对应的管辖区间,这个可以用单调栈 \(O(1)\) 求。对于每个点向其管辖区间内连边,只要能从 \(p_i\) 走到 \(q_i\) 那么就代表可以。
现在考虑怎样满足相同颜色可以互相到达的情况,这个其实很好想,我们只需要按照魔力值进行连边而不按下标连边即可。但是如果暴力连边跑 DFS 是 \(O(n^3)\) 的,会 TLE。
考虑魔力值 \(i\) 对应的一个管辖区间内两个魔力值 \(j,k\),满足 \(k\) 是管辖区间中最大的魔力值(注意这里实际上有左右两个)。显然我们会从 \(k\) 向 \(j\) 连边,如果我们此时再从 \(i\) 向 \(j\) 连边显然是浪费的,所以我们只需要对 \(i\) 向 \(k\) 连边即可,也就是说对于一个点 \(i\),只需要从 \(a_i\) 向其左右管辖区间中最大的魔力值连边即可。这样跑 DFS 的复杂度就是 \(O(n^2)\) 的了。
显然合并可到达点的过程可以使用 bitset 优化,复杂度降为 \(O(\dfrac {n^2} w)\)。
T4
显然最后的得分只与得到的星有关,只要知道得到的总星数就可以知道最后的段位。
- 注意到 \(i\) 段 \(m\) 星与 \(i+1\) 段 \(0\) 星得到的星数一致,实际上这取决于最后一局的输赢。最后一局赢位前者,否则为后者。
考虑求出最后的星数,实际上这只与获得的报星额度有关,也就是只与连续段长度有关。具体的,设每一个连续段长度为 \(L_i\),则最后星数为:
因此我们只需要求出 \(\max \sum \lfloor\dfrac {L_i}{d}\rfloor\) 即可。考虑 dp。设 \(dp(i,j,0/1)\) 表示前 \(i\) 局赢了 \(j\) 局,最后一局输 / 赢的获得的最大报星额度。那么方程如下:
- \(dp(i,j,0)=\max\{dp(i-1,j,0),dp(i-1,j,1)\}\)。
- \(dp(i,j,1)=\max\{dp(i-1,j,0),dp(i-1,j,1),\max(dp(i-d,j-d,0),dp(i-d,j-d,1))+1 \}\)。
方程比较显然,复杂度为 \(O(nm)\)。最后分类讨论最后一局的输赢并计算答案即可。

浙公网安备 33010602011771号