比赛记录(41~50)

41 CSP-S 模拟赛23

1 得分

题目 T1 T2 T3 T4 总分
得分 60 30 60 10 160

排名:rank 3

2 题解

T1

考虑对数组求前缀和,可以得到 s0,s1,,snn+1 个值。注意到在取模 n 意义下它们只有 n 个取值,有由抽屉原理得,必存在一对 i,j(i<j),使得 sisj(modn)。那么区间 [i+1,j] 就是符合要求的子集。

T2

容易发现,当所有书的本数都不超过其它书的本数加一时,绝对能放完。因此我们只需要判断是否有书的数量大于了 n2 即可。但是由于 n 达到了恐怖的 5×107 且空间仅有 16MB,因此无法存下所有 ai。考虑到我们可以用这样一种方式求出大于 n2 的书:

  • 设两个变量 cnt,id,初始 cnt=0
  • cnt=0 时,将 id 赋为 ai
  • 否则若 id=aicntcnt+1;否则 cntcnt1

由于如果存在一本书数量大于 n2,那么其 cnt 一定不会被别的书减掉,于是此时的 id 就是这样的书。我们再回去跑一边就可以求出其出现次数 tot

最后我们计算答案。如果不存在书数量大于 n2,则输出 0。否则答案应该是这本书出现次数减去其它书出现总次数再减一。容易发现这就是 2totn1,输出即可。

T3

Part 1

考虑当所有 d(u,v)>0 时怎么做。考虑两个端点 u,v,若存在一个节点 k,使得 d(u,v)=d(u,k)+d(k,v),那么 w(u,v) 就只需要不小于 d(u,v) 即可,即方案数乘上 (Kd(u,v)+1);否则 w(u,v) 必须等于 d(u,v),方案数不变。

无解的情况有四种:d(u,v)d(v,u),d(u,u)1,d(u,k)+d(k,v)<d(u,v),d(u,v)>K

Part 2

考虑当 d(u,v)=0 时上面做法为什么会错。在判掉无解情况后,对于一个所有 d(u,v) 都是 0 的联通块来讲,我们会认为它的每一条边都可以不选,但是显然如果每一条都不选会炸。考虑将这样的联通块缩点,然后分块内块外考虑。

对于块外的情况,与上面 d(u,v)>0 的类似,只是边是重边,只需要带上次方即可。然后考虑块内的情况,实际上我们可以看作是将所有不为 0 的边权删掉后,仍能形成联通块的图的个数。这是一个经典的容斥,与 2024.8.12 T3 基本相同。设 gi 表示 n 个点构成的图的数量,fi 表示满足上述条件图的数量。那么有:

gi=(K+1)Ci2fi=gii=1i1fj×gij×Ci1j1×Kj(ij)

T4

Part 1

考虑没有重复数字时怎么做。

我们需要先知道一个性质,就是我们每一次操作后剩下的数不能小于当前的中位数。于是我们先排序,第一次就只能放下 a1。接下来分类讨论:

  • 当原先有奇数个数的时候,此时中位数一定是某一个数 m。考虑新加进来的值 a 以及第一个比 m 大的值 k,那么必须满足 a+m2k,即 am2k。于是如果 (m,m2k] 中已经有数,就可以放当前的最大值;否则就必须放该区间中的最大值。
  • 当原先有偶数个数的时候,此时如果 (m,k) 中已经有数,就可以放当前最大值;否则就只能放 k

我们可以用对顶堆来维护中位数,然后按照上面的规则放即可。

Part 2

考虑有重复数字时怎么做。我们设 M 为整个区间的中位数,那么如果小于等于 M 的数中有重复数字,我们就先放这些重复数字中最大的。假设当前放下的是 ak,接下来我们就依次放 ak1,an,ak2,an1,,直到 1k 全部放完了之后,我们会剩下一段区间的数字(有可能不剩),此时再使用上面 Part 1 的方法即可。

42 CSP-S 模拟赛24

1 得分

题目 T1 T2 T3 T4 总分
得分 25 52 3 10 90

排名:rank 11

2 题解

T1

容易发现,符合要求的边必须存在于所有奇环、不存在于所有偶环。但是所有的环太多了,这个时候就考虑选取一些环来代表。考虑无向图上常见的套路:DFS 树,我们只需要考虑由一些树边和一条返祖边构成的环,然后所有环都可以由它们异或得到。

但是如果暴力维护环复杂度仍然不优,我们可以考虑将边权下放到点,然后树上差分。这样复杂度就是严格 O(n) 了。注意如果只有一个奇环需要考虑加上返祖边的贡献。

T2

容易发现如果肥料能流到当前节点,那么在该节点的根链上所有指向左儿子的点的点权都要比该点点权大、所有指向右儿子的点的点权都要比该点点权小。我们可以考虑先树链剖分,然后现在每一个点都有了在树链上对应的指向。接下来开一颗线段树,维护区间内所有指向左儿子的点的最小值、所有指向右儿子的点的最大值。

考虑交换子树怎么做,其实我们只需要交换区间内指向左儿子的点的最小(最大)值 和 指向右儿子的点的最小(最大)值然后打标记。但是上面我们只维护了两个信息,所以实际上我们要维护的是区间内所有指向左儿子的点的最小 / 最大值、所有指向右儿子的点的最小 / 最大值。

T3

考虑我们要让 E 的最大值最小,所以 E 要尽可能小。于是我们就可以这样规定:对于两点 i,j,让 Ej=Ei+dis(i,j)。显然这样做绝对是满足第二条要求的。

那么考虑整棵树上的情况,实际上就是要求出一个排列 pi,满足 Epi+1=Epi+dis(pi,pi+1),并使得 Epn 最小。实际上这等价于最小化 i=1n1dis(pi,pi+1)。我们转化一下可以得到该式等于 (i=1n1dis(pi,pi+1)+dis(pn,p1))dis(pn,p1)。前一部分的最小值其实就是边的数量乘以 2,即 2(n1)。取法就是直接按照 DFS 序取即可。于是我们现在只需要最大化后面的 dis(pn,p1) 即可,显然我们找到树的直径作为 p1,pn 即可。复杂度是 O(n) 的。

T4

Part 1

我们发现这个操作是一个幂塔的形式,考虑利用扩展欧拉定理,那么我们就可以得到:

ccaiccaimodφ(p)(modp)

我们可以利用递归去求出不同层数的幂塔对应的结果,这样在计算时可以直接调用。

Part 2

考虑上述幂塔的层数,容易发现最后模数会变成 φ(φ(φ(φ(p)))),而实际上这个值最多叠 logp 层后值就会变成 1,此时就没有意义了。因此上述幂塔的层数最多就是 logp 层。

实际上这个结论的用处不止于此。考虑到由于幂塔只有 logp 层,所以一个位置上有效的操作也只有 logp 次,而大于这个次数之后的操作我们可以不管。这就是经典套路了,在线段树上维护出区间操作的最小次数以及区间和,当最小操作次数 =logp 时就不必递归了。复杂度就是 O(nlognlogp),可以通过。

43 CSP-S 模拟赛25

1 得分

题目 T1 T2 T3 T4 总分
得分 100 50 67 40 257

排名:rank 2

2 题解

T1

实际上不难发现,如果点对之间有至少一条出现次数为奇数的边权,那么一定先手必胜。考虑 “至少一次” 不好维护,利用容斥改为总方案数 - 所有边权出现次数均为偶数的方案。

这个东西和 2024.8.8 T3 十分相似,我们可以用 01 串表示该节点到根节点的根链上,每一种边权是奇数还是偶数。那么如果两个点之间的边权满足出现次数均为偶数,则必然会有两个节点到根链的 01 串一模一样。我们可以存下每个节点对应的串,然后组合数简单计算即可。

但是注意到此题中边权即使离散化也有 2×105 种,所以要使用哈希。

T2

通过观察样例,可以得到的是最后我们一定是在某个区间反复横跳去累加答案。但是我们并不能一次就跳到任意一个区间的右端点。实际上,我们应该是第一次先跳到一个 r1,在 [l1,r1] 内横跳,把值积攒够了之后跳到 r2,在 [l2,r2] 内横跳,攒够了之后跳到 r3……

于是我们就可以考虑对区间的右端点进行 dp。设 f(i) 表示跳到 i 的最小次数,g(i) 表示在满足跳到 i 次数最小的前提下,最大的分数。我们可以枚举上一次横跳区间的右端点 j,计算出在上一个区间需要横跳多少次才能攒够到达 i 的分数,以此转移即可。

最后枚举以哪一个区间作为最后横跳的区间然后取最大值即可。

T3

直接给出构造方式。考虑维护出以 x 为根节点的子树中还没有被选到的节点集合 Sx。对于一个节点 u,每一次将 Sv 整体插入 Su 中,当 Su 大小大于等于 B 时将点集内所有点划分到一个省内,省会为 u。接着在剩下的点集中插入 u,继续向上返回。当求解完 S1 之后,将 S1 中的所有节点放到以 1 为省会的另一个省中即可。

下面说明其正确性。显然省要么是一个联通块,要么加上省会后是一个联通块。所以一个根节点的不同子树可以合起来构成一个省。考虑上述添加子集的过程,容易发现,我们返回的 Sv 的大小不会超过 B。而此前 Su 中最多有 B1 个元素,两者合并之后得到的省的大小最大就是 2B1

在最后考虑 S1 的时候我们直接将 S1 中所有节点加入另一个省,此时 S1 中最多有 B 个节点,加入另一个省后最多有 3B1 个节点。发现上述所有的信息都是符合题目条件的,因此该方法正确。

T4

Part 1

发现区间循环右移实际上就是区间平移,因此考虑使用 FHQ-Treap 实现区间操作。那么难点就在于如何判断有无三元上升子序列。考虑三元上升子序列在 FHQ 里有哪几种表现方式,不难发现是如下几种:

  • 左子树 a - 当前根节点 b - 右子树 c
  • 左子树 ab - 右子树 c
  • 左子树 a - 右子树 bc

所以我们需要维护四个值:maxn 表示子树内值的最大值,minn 表示子树内值的最小值,maxn 表示存在一个以 maxn 开头的二元上升子序列且值最大的值,minn 表示存在一个以 minn 为结尾的二元上升子序列且值最小的值。有了这四个值我们就可以方便的维护出区间的答案 tag

问题就在于 maxn,minn 的维护看上去是困难的。

Part 2

(下文以 maxn 举例)

我们发现 maxn 难维护的地方在于左边取一个,右边取一个的情况。显然为了让 maxn 尽可能大,右子树要取到 maxnr 最优。那么理论上只需要找到左子树中最大的小于 maxnr 的值。问题是现在的平衡树是区间平衡树,似乎无法直接求出这个值。

我们考虑这样一件事,如果当前区间的 tag1,我们无需维护 maxn。而如果 tag 不为 1,则说明左子树内不可能存在两个值,满足 i<jai<aj<maxnr。因此,左子树中小于 maxnr 的值按下标排序后一定时单调递减的。利用这个性质,我们实际上就是要找到左子树中最靠左的小于 maxnr 的值,这个就可以直接做了。

那么最后的复杂度就是 O(nlog2n) 的,可以通过。

44 CSP-S 模拟赛26

1 得分

题目 T1 T2 T3 T4 总分
得分 100 100 60 30 290

排名:rank 1

2 题解

T1

考虑维护两个 dp 数组,设 f(i) 表示走 1i 的最短路的路径数量,g(i) 表示走 1i 的最短路长度加一的路径数量。那么有转移:

  • g(i)g(i)+f(j)(disj=disi)
  • g(i)g(i)+g(j)(disj+1=disi)

按照 dis 从小到大转移即可。

T2

显然网的左端点在鱼上最优。考虑枚举撒网的左端点的鱼的编号 i,那么过了 t 时刻后能被捕到的鱼 j 要满足:

xj+tvj[xi+tvi,xi+tvi+a]

也就是:

(xjxi)+t(vjvi)[0,a]

于是我们可以解出每一条鱼被捕上要满足的时间所在的区间 [lj,rj]。问题就变成选一个点 t,使得所有包含 t 的线段的权值之和最大。显然先离散化,然后上一个类似扫描线的东西维护区间总和然后取最大值即可。复杂度 O(n2logn)

T3

考虑将所有 <k2 和所有 k2 的数分开考虑。我们将数列排序,考虑将一个 a<k2 插入序列,那么其相邻的 b 一定满足 bka。我们先把满足条件的 b 插进去,然后在插入 a 即可。

现在考虑限制条件,当我们插入一个 a 之后,显然之后新插入的值都不能与他相邻;而插入一个 b 后,之后插入的值都可以与他相邻。那我们可以考虑维护当前序列的空位 s。插入 as 减一,否则 s 加一。答案就是每一时刻 s 的积。

T4

考虑会被染上颜色的点满足什么条件,显然是:

  • 子树内有关键点。
  • 子树外有关键点。

考虑利用容斥,用满足第一个限制的点的个数减去满足第一个限制但不满足第二个限制的点的个数。

Part 1:

后面一部分是好计算的,显然就是区间内所有点的 LCA 的所有祖先。区间内所有点的 LCA 实际上就是 DFS 序最小和最大的点的 LCA,直接计算即可。

Part 2

考虑离线处理第一部分。我们每一次取移动右端点,设 posi 表示 i 子树内在 [1,R] 之内的关键点的最大的下标。那么对于一个询问 [L,R],显然满足 posiL 的点是有贡献的。

我们考虑从 RR+1pos 有什么变化,显然就是 aR+1 的所有祖先的 pos 都会变成 R+1。那么这就相当于树链覆盖。这可以拆成树链剖分和区间覆盖,而后半部分可以使用珂朵莉树直接维护。最后我们考虑如何维护答案,不难发现,当我们进行树链覆盖的时候,只有 posx<LR 的所有 x 会对询问 [L,R] 产生贡献,我们就可以利用树状数组去维护这个贡献。

具体的,当我们找到一段区间 [dfnL,dfnR] 满足 pos<R 的时候,在树状数组上的 pos+1 处加上 dfnRdfnL+1、在 R+1 处减去 dfnRdfnL+1。这样查询的时候直接求 L 的前缀和即可。

复杂度是 O(nlog2n) 的,跑的飞快。

45 CSP-S 模拟赛27

1 得分

题目 T1 T2 T3 T4 总分
得分 30 20 30 40 120

排名:rank 4

暑假最后一场给了坨大的(指题目难度)。

2 题解

T1

容易列出 dp 方程:设 dp(i) 表示最后一场取到 i 的最大价值,那么状态转移方程就是:

dp(i)=maxj<i,ajai{dp(j)+(ij1)(i1)2}+ai

这样做的复杂度是 O(n2) 的,考虑优化。不难发现这是一个 1D / 1D 动态规划,考虑斜率优化。按照传统艺能推式子,可以得出:

p(i)=2dp(i)i2ip(j)p(k)jk>2i

这个式子非常简单,我们维护上凸包然后单调队列即可。不过发现这里还有其他的限制条件,就是下面的 j<i,ajai,似乎不能简单的去用斜率优化。考虑另一件事,1D / 1D 动态规划的另一种优化方式就是 CDQ 分治,而显然这里的限制条件就是一个二维偏序。所以我们可以考虑利用 CDQ 分治去满足这两个限制条件,然后利用斜率优化 dp 处理区间之间的贡献,复杂度就可以做到 O(nlog2n)

(尽管这个难度放 S 组 T1 已经很离谱了,但是它确实是最简单的题)

T2

Part 1

考虑 k8 的时候怎么做,考虑直接爆搜然后加上记忆化,问题就在于怎么记忆化。

考虑记录出每个元素出现的次数 cnti,然后统计出每一种 cnti 的出现次数,显然这两个值都是 8 的。我们考虑去记录后面一个值的状态,具体的,设 cnti 的出现次数为 xi,那么我们维护一个十六进制数 S=xi×16cnti1 表示当前状态(十六进制只是为了方便,因为可以用 1 << 4 表示),将这个作为记搜的标志。

但是这里会存在一个问题,就是当最后一个元素在序列中的个数不同时,答案是不同的。而且会发现当且仅当最后一个元素在序列中出现的次数不同时才会不同,其他元素没有影响。所以我们还要记录最后一个元素出现的次数 lst,这样记搜过程中的状态就可以记录为 Vlst,S

Part 2

考虑 k>8 的时候怎么做。通过尝试可以发现,当 k>8 的时候序列总数已经远超过了 1018 的范围,也就是说此时前面的部分一定是形如 ababa...bacdcdc...dcefef...fe.... 的,我们直接每一次输出一段形如 abab...ba 的前缀都会使 k 减少 2,不断循环直到 k8,然后利用上面的爆搜求解剩下的部分即可。

T3

考虑到什么时候会存在 |A[i][m]A[i+1][m]|>0,不难发现此时 A[i][m+1k] 应该恰好是 n(km)+1n,因此实际上我们可以忽略这一部分,题目中所给的 n,k 实际上相当于 n(km),m。也就是说我们只需要考虑 m=k 的情况即可。记其答案为 f(n,k)

考虑第一个元素 A[i][1] 的值,它可以取到 [1,n(k1)]。此时会出现两种情况:

  • A[i][1]=A[i+1][1]
  • A[i][1]+1=A[i+1][1]

那么大分类讨论一下两个情况。先考虑第二种情况,即 A[i][1]+1=A[i+1][1]。设 A[i][1]=x,我们考虑此时会发生什么:

  • 显然 A[i][2m] 应该是 n(k1)+1n
  • 显然 A[i+1][2m] 应该是 x+2x+k

于是此时 |A[i][m]A[i+1][m]| 就应该是 nxk。于是枚举 x,可以得到这一种情况的总答案是:

i=1nknik=i=1nk1i=(nk2)

g(n,k)=(nk2)。实际上它就相当于是将 nk+1 分成 3 个正整数的方案数。

(注意这里取不到上界 nk1,因为取到上界之后不可能存在更大的 A[i][1] 了)。

然后考虑 A[i][1]=A[i+1][1] 的情况,仍然考虑枚举 A[i][1],则答案应该是:

i=1nk+1f(ni,k1)

于是可以得到 f(n,k) 的递推式:

f(n,k)=g(n,k)+i=1nk+1f(ni,k1)

此时可以 O(n2) 计算了。考虑优化,我们发现这个式子实际上只是一堆 g(m,q) 的求和,我们只需要求出所有 g(m,q) 的系数即可。不难发现,从 nm,kq,实际上就是在 n 这个数的基础上减去了 nm,总共减了 mq 次。也就是将 nm 分成 mq 个正整数的方案数。但是此时我们还是要枚举所有的 m,q 才能得出答案,于是考虑只枚举 q,然后求出所有 qmng(m,q) 的贡献。

考虑到在枚举 q 之后,实际上 m 就相当于将 nq+1 分成 nmmq+1。 而它的系数应该是将 nm 分成 mq 个正整数的方案书,它的值应该是将 mq+1 分成 3 个正整数的方案数。合起来就相当于将 nq+1 分成 mq+3 个正整数的方案数,也就是 (nqmq+2)

暴力枚举 q 然后利用组合数求解即可。但是注意到 q=1g(m,q) 不能直接这样计算,单独拎出来考虑即可。

T4

考虑到我们要求的是复数之和的模长,因此考虑将所有复数放到复平面上考虑,这样我们就只要求出 n1 条向量相加的模长最大值。

考虑枚举最后相加得到的向量的幅角 θ,定义一条向量的贡献是该向量在这个结果向量上的投影的长度,此时认为其他方向上的贡献会被抵消。

实际上是不会抵消的,但是如果枚举的 θ 不能使其他的方向被抵消,那么一定会存在一个更优的 θ 使得贡献被抵消,那么答案会更优。

于是我们就考虑枚举这个 θ,将所有向量按照其投影长度排序,然后按照这个进行 Kruskal 最大生成树,取所有 θ 的答案最大值即可。

46 CSP-S 模拟赛28

1 得分

题目 T1 T2 T3 T4 总分
得分 100 25 20 0 145

排名:rank 6

2 题解

T1

简单题。考虑对 a 分解质因数,然后将里面所有出现奇数次的质因子找出来,相乘得到 b。此时只要给 b 上乘上一个平方数即可。这个利用左端点 l 简单计算一下即可。

T2

首先注意到 lcmgcd 的倍数,又由于 gcd+ lcm=m,因此我们只需要枚举 m 的因数即可求出 gcd,进而也就求出了 lcm。显然复杂度是 O(m) 的。

假如当前枚举的 gcdglcml,那么实际上就是求满足 gcd(S)=g,lcm(S)=lS 总数。发现它可以转化为求 gcd(S)=1,lcm(S)=lgS 总数。

接下来设三个函数:

  • f(x) 表示满足 gcd(S)=1,lcm(S)=xS 总数。
  • g(x) 表示满足 gcd(S)x,lcm(S)=xS 总数。
  • h(x) 表示满足 gcd(S)x,lcm(S)xS 总数。

显然 f(lg) 是最终答案。

同时令 d(x) 表示 x 的约数个数,那么易得 h(x)=Cd(x)n。同时我们会有:

h(x)=dxg(d) g(x)=dxμ(xd)h(d)

上式就是基础的莫反,那么同理我们可以得到:

g(x)=dxf(d) f(x)=dxμ(xd)g(d)

注意 g(x)f(x) 的关系式的求法,实际上枚举的 d 就是当前 Sgcd,给每一项都除以 dlcm 就变成 xdgcd 也变为 1,显然此时方案数就是 f(xd)。而实际上就是对于每一个 df(d) 并累加。

于是就得到:

f(x)=dxμ(xd)g(d)=dxμ(xd)exμ(xe)h(e)

直接暴力枚举 x 的因数求解即可。复杂度 O(d(m)3) 级别,但是远远跑不满,可以通过。

T3

题面的限制其实是类 LIS,所以首先考虑 dp。设 f(i) 表示从左往右以 i 为结尾的类上升子序列的个数,g(i) 表示从右往左以 i 为结尾的类上升子序列的个数。答案应该是 f(i)×g(i),不过发现这样是有问题的。

原因在于当我们取到的峰值周围有与它相同的值的时候,我们的计算会重复。因此钦定峰值只能取到最先出现的那个。那么我们就需要利用容斥将不合法情况减去。

容易发现这个做法直接做是 O(n2) 的,考虑优化。下面我们只针对 f 进行说明,g 是对称的。

我们可以记录下当前以 si 为最后一个数的类上升子序列数量 p(si),那么对于当前要求的 f(i),枚举 i 的二进制下子集 S,然后就有 f(i)=1+p(S),然后将 p(si) 加上 f(i)

发现这个时间复杂度是 O(n2m),还不如上面那个优。我们发现操作的本质就是对 p 数组修改和查询,而此时我们修改的复杂度是 O(1) 的,查询是 O(2m) 的。发现两者的复杂度差异巨大,考虑平摊复杂度。

具体的,我们将 p 拆成 p(i,j),表示以二进制下前半段为 i,后半段为 jsi 为结尾数字的序列数量。那么我们在修改的时候枚举 j 的补集的子集,对这些部分都加上 f(i);而查询的时候只需要枚举前半段 i 的子集并累加即可。这样我们修改的复杂度是 O(2m2),查询复杂度也是 O(2m2)。总复杂度为 O(n2m2),可以通过。

T4

过于抽象,没改。

47 CSP-S 模拟赛29

1 得分

题目 T1 T2 T3 T4 总分
得分 100 70 70 10 250

排名:rank 2

2 题解

T1

40 pts:爆搜即可。

another 40pts:

看到 n18,自然想到状压。考虑到原二元组中的 b 就是个废物东西,因为每一次合并的代价都是固定的,所以只需要考虑 a 即可。具体的,考虑设 f(S,x) 表示当前已选二元组状态为 S,算出 ax 的序列代价和,g(S,x) 表示当前已选二元组状态为 S,算出 ax 的方案数。那么转移方程如下:

f(S,x)=f(S,x)+g(S,x)×k×xg(S,x)=g(S,x)

设当前新加入的二元组是 ap,那么 S{p} 就是 S,而对应利用 x,ap 算出来的 a 就是 x。暴力转移即可,复杂度 O(ain2n)

100 pts:

考虑到这样一个结论:对于序列 a1,a2,,an,其最终算出来的 a 必然是 aiai+1。也就是说上面式子中的 x 必然可以写成 ap+q(q{0,1})。所以就使用三元组 (S,p,q) 代替原先的二元组 (S,x) 即可。复杂度为 O(n22n),可以通过。

实际上根据上面的性质,不难推出转移的最大值就是 max{ai}max{ai}+1。用 f(S,0/1) 记录取到的是加一还是不加一的然后按照同样方式转移即可,复杂度可以降为 O(n2n)

T2

60 pts: 分两部分暴力模拟即可。

100 pts:

考虑到有绝对值,容易想到进行拆分。问题转化为求一个点左上、左下、右上、右下的卫星贡献总和。

我们考虑对所有 x 坐标与 y 坐标离散化,整个坐标系就变成了 n×n 的。接下来考虑利用前缀和。以左上距离,设 sum(i,j) 表示离散化后点 (i,j) 左上角的所有卫星到 (i,j) 的贡献之和,朴素求解即可。接下来找出查询点在离散化后坐标系上的相对位置,它应该在一个矩形中。此时找到矩形左上角,算出对应的 sum,然后再乘上从左上角到这个点的贡献即可。

对四个方向都做一遍然后累加即可。由于要多次求幂且底数不变,可以使用光速幂进行优化。

T3

我们考虑这样一种暴力:在所有查询中找出本质不同的查询二元组 (x,y),利用哈希暴力计算出所有二元组的答案,然后输出即可。

实际上,这个暴力在时间复杂度上是完全正确的,证明如下:

设字符串总长为 L,考虑一个阈值 B

先看所有长度 >B 的串,显然最多有 LB 个。那么考虑将他们之间两两的答案计算出来,易得这一部分的复杂度是 O(L2B)

然后是所有长度 B 的串,他们最多会有 Q 次查询,单次查询复杂度 O(B),因此这一部分复杂度为 O(QB)

两部分复杂度总和为 O(L2B+QB),可以知道 B=LQ 时式子有最小值 O(LQ)

显然上述算法的本质就是暴力算法,两者复杂度一致,因此“暴力”算法的复杂度应该是 O(LQ) 的。

不过实际交上去会发现 WA 99pts,只错了一个。原因在于如果只用了自然溢出的单哈希会被卡,需要使用双哈希进行计算。

T4

抽象题目。

Part 1:

初步的思考是:我们希望求出对于一个节点 i,能够连到的离他最近的点编号与他的差。如果这个值是 p,那么表明连边是以 p 为周期进行的,于是答案就是 p

那么考虑连边的实质,实际上就是从 i 开始,每一次加上或减去 Si,最后得到的值,也就是 aiSi。显然由裴蜀定理的,这个式子的值是 gcd(Si) 的倍数,因此 p 理论上就是 gcd(Si)

然而这只是理论上的,因为裴蜀定理求出的解可能必须要跳出 n 的范围才行,于是我们必须要考虑怎样的 Si 不会跳出去。

Si 的上限是 M。考虑当前跳跃的距离 v,初始时 v=0。接下来分类讨论可得:

  • v>M 时,一定有 Si 满足 vSi0,满足条件。
  • vM 时,一定有 Si 满足 v+Si2M1。此时若要满足条件,则必须有 v+Si<n,因此必然有 2M1<n,即 2M1n1,解得 Mn2

因此得出如果想要不跳出去,一定要满足 Sin2。于是我们将所有 n2 的数字取出来求出 gcd,得到当前的答案 d

Part 2:

现在我们先放下思绪,冷静考虑一下。上面的分析都保证了里面有 n2 的元素,但是如果全是 >n2 的元素应该另外处理。

考虑当所有 Si 都大于 n2 的时候,必然会存在一些节点,他们不管怎么挑都不可能跳在边界内。换句话说,他们无法与任何点连边,考虑将这样的点删去。

显然这样的点 x 对所有 Si 都满足 xSi<1x+Si>n。设 Si 中最小的为 V,那么必然会有 xV<1x+V>n。也就是 nV<x<V+1。不难发现这样的 x 共有 2Vn 个,记这个数为 B

此时仍然容易发现,所有的 Si 跳的时候必然都跨过了这 B 个数的区间,因此可以将所有 Si 都减去一个 B,同时 n 也减去 B,给答案加上 B,问题没有变化。

考虑原先的最小值为 V,现在就变为 VB=nV。同时此时的 n 变为 nB=2(nV)。于是新问题中的 Si 必然会有至少一个满足 Sin2,也就转化为了 Part 1 的情况。

Part 3:

现在回到 Part 1 的情况,此时我们已经处理完了所有 Sin2 的数的贡献,现在处理剩下的。

考虑如果存在 Si>n2,且 d+Sin 的数,那么此时相当于我们可以从当前组再向后跳 Si,那么此时他扩展到的组会有一个距离当前位置最近的点,不难发现这个点与当前点的距离是 gcd(Si,d)。也就是说现在新的 d 会变成 gcd(d,Si)

然后剩下的数就是 Si>n2d+Si>n 的数。那么此时能够选择开始跳的第一个节点的最大节点是 nSi,这个值最大取到 d。也就是说左边开始的点取不到一个完整的周期。此时仿照 Part 2 中的思路,此时 Si 一定会跨过中间大部分区间,跳到序列末尾的节点。

那么我们就可以将中间的部分砍掉,显然剩下的部分应该保留的长度是前面的 d 个点和最后的 nmodd 个点。也就是说新的 n 等于 d+(nmodd)。同时所有的 Si 就要减掉中间的部分,也就是新的 Si 应该是 nn+Si

此时问题转化成了一个新的子问题,当然此时除了 Si 之外应该还要在集合中加上 d,以满足原先的周期要求。

接下来继续递归求解即可。

现在就是考虑如何维护这些东西了。实际上我们发现,当我们将所有元素排序后,每一次取出的要变动的部分是从头开始的连续一段。因此可以用 set 存下当前所有的 Si

接下来考虑复杂度,其实只需要考虑递归次数。递归次数实际上是 O(logn) 级别的,加上 set 复杂度为 O(mlog2n)​。实际上远远跑不满,所以可以通过。

48 CSP-S 模拟赛30

1 得分

题目 T1 T2 T3 T4 总分
得分 100 60 40 30 230

排名:rank 2

2 题解

T1

容易发现,我们可以找出当前数字中出现的极长连续段。设其长度为 L,则必须要删去 L2 个。考虑到 ai 只有 5×105,因此开值域线段树维护连续段信息即可。

T2

首先考虑在正常线段树上跑出区间 [x,y] 被分割出的区间。然后问题就转化为求根节点编号为 p,且长度为 l 的线段树子树编号之和。

首先较为显然的一点是,这样的树一定是一颗满二叉树下面挂几个叶子节点。上面的满二叉树部分是好计算的,枚举每一层然后等差数列直接算就行。问题就在于下面这些叶子节点。

我们假设满二叉树的最后一层有 a 个节点,且我们需要在 b 个节点上挂左右子树。那么实际上根据手玩发现,它可以拆成两个子问题:

  • 最后一层有 a2 个节点,且我们需要在 b2 个节点上挂左右子树的编号和。
  • 最后一层有 a2 个节点,且我们需要在 b2 个节点上挂左右子树的编号和。

实际上就是分成左右两半分治。但是发现暴力分治复杂度不好,观察上面式子发现左右分治的时候答案最多只会在左边多一个,而且多出的这一个可以算出来。所以我们就可以把分治复杂度降为 log 复杂度。

考虑最后复杂度。暴力跑分割区间是 O(logn) 的,而求解编号和又是 O(logn) 的。所以总复杂度 O(Tlog2n),可以通过。

T3

发现有取 max 操作,不方便维护,我们分类讨论一下。设 ap,bp 表示法杖信息,aq,bq 表示魔咒信息。

  • ap+aq>bp+bq 时,apbp>bqaq
  • ap+aq<bp+bq 时,apbp<bqaq

np=apbp,mq=bqaq。如果确定了 npmq 的关系,就可以知道是 a,b 中的谁做贡献。

那么考虑利用线段树。我们对于一个法杖或魔咒,在其对应的 npmq 位置维护 ap,bp 最小值或 aq,bq 最小值。在合并区间时,根据上面推出的大小关系可以得出,当前区间的答案应该是左区间 bp 加上右区间 bq,或者左区间 aq 加上右区间 ap

时间复杂度是 O(QlogV) 的,其中 V 是值域。

T4

30 pts: 显然考虑朴素区间 dp 即可。

100 pts:

考虑到决策一定是向左一段 - 向右一段 - 向左一段 - 向右一段 - …… 于是可以直接设 dpi 表示走到 i 的花费。这里我们定义花费为当前已有的花费 + 从当前状态出发到剩下每一丛杂草所需的花费总和。

接下来考虑转移方程。显然有两种操作,向左或向右。以向左为例,我们假设从 ji,由于我们已经求出在 j 处从 j 开始走到 [i,j1] 中所有杂草所需要的花费之和,所以将这一部分花费挪到当前已有花费即可。而对于[j+1,n] 这一段区间的杂草花费,我们每向左走一段,每一丛杂草所需的花费都会加 2。所以转移方程为:

dp(i)=dp(j)+2(nj)(sjsi)

同理可以得到向右的转移式:

dp(i)=dp(j)+2(j1)(sisj)

根据定义,初始状态就是 dp(k)=|sksi|,其中 siDi 前缀和。

那么实际上由于该 dp 的特殊性,我们可以用类似 Dijkstra 的形式转移。意思是说,我们对于未被扩展过的 dp(i) 找出最小的一个进行扩展,然后将扩展后的值加入。最后答案是 min(dp(1),dp(n))​。

这个做法复杂度和朴素 Dijkstra 一样,不够优。考虑优化。我们发现如果 i<jk,那么一定有 dp(i)dp(j)。此时发现整个算法中被更新的 dp 值一定是包含 k 的一段区间 [l,r],且如果要扩展 dp 值,只需要考虑 dp(l1)dp(r+1) 即可。

此时取最小值的过程变成了 O(1),但是扩展区间和转移的总复杂度还是 O(n2) 的。我们发现瓶颈应该在于对于新扩展点找最优决策点,而我们发现这一部分是可以斜率优化的。因此复杂度就降为 O(n)​ 了。

49 CSP-S 模拟赛31

1 得分

题目 T1 T2 T3 T4 总分
得分 68 100 8 20 196

排名:rank 3

2 题解

T1

其实是线段树的套路题。

考虑维护线段树,枚举一个端点,然后在线段树上存储另一个端点信息。在这道题中,我们在线段树的 x 下标处维护 mex(l,x),其中 l1 开始向后枚举。

那么我们就先求出所有 mex(1,x) 的答案并建出线段树,接下来考虑 l 挪动的时候对区间有什么贡献。不难发现,当 ll+1 时,令 al 下一次出现的位置为 nxt(没有则为 n+1),那么所有 x[l,nxt)mex(l,x) 的值都应该和 al 取最小值。

考虑到对于固定的左端点,mex 是单调递增的,因此在区间中 >al 的部分是连续的。考虑二分求出这个区间,然后就可以转化为区间覆盖。这样就可以直接用线段树解决了。

由于二分的时候要再加上一个线段树的复杂度,因此总复杂度是 O(nlog2n) 的。

T2

首先我们要看到这样一个结论:

对于最优方案,一定是通过一个断点将序列断开,然后从后往前遍历,如果当前位置上有钱,就将这个位置上所有的钱平均放到最后面的位置。

手摸样例然后感性理解是比较容易得到的。但是难点不在这里,我们发现这样断开的序列是要有要求的,显然要求如下:

对于每一个长度为 i 的后缀,其后缀和必须小于 i

显然只有满足这个条件,才能使得每一次往后挪动钱的时候能放得下。然后就是考虑怎样求满足条件的序列。我们还是从后往前扫,如果有一个时刻不满足上面的条件了,就重新开始计算。直到扫到的序列长度到达了 n,那么这就是合法的序列。上述过程类似于双指针,复杂度 O(n)

最后还有一个结论:

对于最优方案,不管从什么地方断开,结果一样。

因此只要找到一个合法序列我们就直接进行计算,输出结果即可。复杂度 O(n)

T3

傻逼大样例还我 AC!!!!!!!!!

我们考虑设 fi 表示当 A 先手时 A 拿到最后一个的概率,gi 表示当 B 先手时 A 拿到最后一个的概率。

现在考虑怎样转移。我们考虑从 fi1,gi1 转移到 fi,gi,也就是去考虑第一步双方的决策。由于 A 先手,所以从 A 的角度考虑,如果 fi1gi1,那么这一轮让 B 拿到,下一轮自己先手胜率更高,于是 A 想投的应该是反面,而 B 为了避免自己拿到也会投反面;否则如果 fi1<gi1,那么这一轮让自己拿到胜率更高,同理可得此时两人都想投正面。

那么现在问题就在于要计算出第一轮双方不同决策概率。由于双方可以无限投,所以需要利用等比数列求和来做,最后求出的概率如下:

  • 当两人都想投正面时:
    • A 先手时:
      • A 拿到这枚石子的概率为 pp+qpq
      • B 拿到这枚石子的概率为 1pp+qpq
    • B 先手时:
      • A 拿到这枚石子的概率为 1qp+qpq
      • B 拿到这枚石子的概率为 qp+qpq
  • 当两人都想投反面时:
    • A 先手时:
      • A 拿到这枚石子的概率为 1p1pq
      • B 拿到这枚石子的概率为 11p1pq
    • B 先手时:
      • A 拿到这枚石子的概率为 11q1pq
      • B 拿到这枚石子的概率为 1q1pq

然后根据这个就能列出转移方程,此时暴力做是 O(Tn) 的,考虑优化。

发现如果没有 fi1,gi1 大小差异造成的转移方程系数的不同,我们显然可以直接矩阵快速幂,然而现在转移的时候有大小限制。不过容易发现的是,如果 fi1gi1,那么本次转移后得到的数一定满足 fi<gi(作差后判断正负形易得);而反过来也一样。也就是说转移是在两种状态来回交替的,那么我们就可以继续使用矩阵快速幂优化了。

时间复杂度 O(Tlogn)

T4

考虑到本题中积水的大小和左右两边柱子的最大值都有关系,因此考虑拆开来做。设 f(i,j,k,0/1) 表示考虑前 i 根柱子,前面柱子中剩下的最大值是 j、且假定后面存在至少一个高度大于等于 j 的柱子,当前抹平了 k 个柱子所能得到的积水面积为偶数 / 奇数的方案数。

接下来考虑转移,我们使用刷表法。

  • 假设 jai+1

    那么显然 ai+1 抹或不抹没有区别,因此 f(i+1,j,k,0/1)f(i+1,j,k+1,0/1) 都加上相应值即可。

  • 假设 j<ai+1

    此时抹不抹就有区别了。抹的时候还是转移到 f(i+1,j,k+1,0/1),但是不抹的时候应该转移到 f(i+1,ai+1,k,0/1)

但是发现 j 难以存储,即使离散化后复杂度也有 O(n2k)。发现第三维 k 的数量极小,只有 25,那么我们的 j 理论上也只有最多 26 种取值,所以将前面 i 个数中的前 k+1 大的数维护出来,然后 j 只需要存储编号即可,复杂度降为 O(nk2)

我们再对反向的序列再做一遍 dp,得出 g(i,j,k,0/1)

最后考虑答案。我们枚举剩下的柱子中最高的柱子,然后将两边的方案数乘起来累加即可。注意直接算会算重,因此我们需要规定只有一边可以取等,另一边不行。

50 加塞(vp)

1 得分

题目 T1 T2 T3 T4 总分
得分 100 60 20 15 195

场外 vp,没有排名。

2 题解

T1

首先模拟样例会发现选择的点是在排序后相邻的,然后发现的确这样选最优。证明不难。

考虑怎么求解,使用 dp,设 dp(i,j) 表示前 i 个点中选出了 j 条边的方案数,转移方程即为:

dp(i,j)=min{dp(i1,j),dp(i2,j1)+aiai1}

注意 a 要先排序。最后答案为 dp(n,m)

T2

策略并不难想,我们从头开始遍历,然后如果当前位置上的值可以更小就删掉。于是有以下策略:

  • 如果 ai>ai+1>0,那么显然将 ai 删掉即可。
  • 如果 ai>0,ai+1=0,考虑此时还没有填的最小数 x,显然 ai+1 要填这个数。自然如果此时 ai>x,那么就要将 ai 删掉。
  • 如果 ai=0,考虑此时 i 位置的后缀最小值 y,如果当前要填的数大于 y,就将 y 删掉即可。

按照这个策略删即可。如果都不满足那么就直接删第 n 个数即可。

那么我们维护一个当前没有出现的数的集合即可,可以用 set,复杂度 O(nlogn)

T3

首先考虑边权不为 0 的情况。

我们考虑牛牛的策略什么时候会错。实际上,简单手玩可以发现,我们建出当前点集虚树,如果虚树中有原先点集中没有出现的点的度数大于 2,那么就会直接假掉。否则度数为 1 时,直接删掉不会影响答案;度数为 2 时,将这个点的两端连起来即可。称使做法假掉的点为关键点。

我们考虑从后往前删除每一个点,然后按照上面的策略维护关键点。当前仅当关键点数量为 0 时不会假。

接下来考虑有边权为 0 的情况。

边权为 0 会带来这样一种问题:边权为 0 的边即使重复计数,也不会对答案造成影响。所以考虑将边权为 0 的连通块合在一起考虑,相当于把它们缩成一个点。这样就可以排除影响了。

T4

讲得很清楚了

posted @   UKE_Automation  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示