2023解题报告(CSP-S后)

2023.10.29

1.sequence

算法:exgcd

给定 n,a,b,c,d

有长度为 n 的序列 A,B 满足 Ai=aimodc,Bi=bimodd,求:

i=1n[Ai=Bi]

可以发现 A,B 两个大序列都有循环节,但直接计算两个循环节的大循环节时间复杂度难以接受。借助循环节中的数字各不相同的性质,我们可以将循环节中每个数的位置记录下来,对于每个数字进行计算,可以用 exgcd 解决。

2.graph

算法: Kruskal 重构树

给出 n 个点,m 条未联通的边,第 i 个时刻会联通第 i 条边,多组询问,求使 [l,r] 的点两两联通的最小时刻。

Kruskal 重构树:

  • 用处:维护路径最大最小值的树,

  • 性质:任意两点的 lca 的点权为其路径的最大/最小值

  • 建树方法:按照边权从小到大/从大到小进行生成树,对于原图上的两点 u,v 的祖先新建一个点 s 成为 u,v 的祖先,且 s 点权为 (u,v) 边权。

这道题我们直接以时刻为每条边的边权,当 [l,r] 两两联通时,一定有 (l,l+1)...(r1,r) 两两联通,我们直接求这些相邻点在重构树上的路径最小值,再用 st 表或线段树维护区间最大值即可。

2023.11.02

1.AOE

算法:st表+树状数组

定义 f(d) 为最小的 i 使得:

j=d(i1)+1diaj=0

即对一个数轴按块长为 d 分块,然后问最靠前的一个和为 0 的块是第几个。

每次加入一个数 x 或询问 i=lrf(i)

考虑离线计算贡献,我们可以先记录下每个数出现的最短时间,再建 st 表维护区间最大值,发现 [1,n] 总共分出的块数是 O(n log n) 级别的,所以我们直接枚举每个块,记录一个前缀最大值,即当前块出现的最早时间,将其记录到对应的时间点上。

最后我们便利询问的时候,每到一个时间点将生效的块在树状数组中记录,最后对于每个询问变为了树状数组区间加操作。

2.stamps

算法: 动态规划dp+单调队列

给出 n 个区间,问选择最多 k 个区间的最大并。

发现当一个区间被另一个区间覆盖时,选较大的区间不会更劣,所以我们先去除被覆盖的区间。发现没有覆盖关系时以 lr 从小到大排序后两者均单调递增,我们可以设 dpi,j 表示选到第 i 个区间时已经选了 j 个区间的最大并,我们考虑两个区间的两种关系去转移:

  • 对于与当前区间没有交集的区间,前缀最大和转移

  • 对于与当前区间有交集的区间,r 越小答案更优,因为交集更少,单调队列转移

答案即为 dpn,k,时间复杂度 O(nk)

2023.11.03

1.race

算法:长链剖分

给出一个二叉树,对于 m 个叶子节点附上 [1,m] 的互不相同的值,每个点的权值为其子树叶子节点的最大值,最大化点权和。

我们发现点的权值在树上是链状出现的,所以可以用长链剖分将链从长到短分离出来,再从大到小赋值即可。

2023.11.04

1.Karma

算法:排序

n01 字符串,求重排后的最少逆序对数。

我们先从逆序对数的计算方式入手:假设我们当前选择的字符串中有 sum1,现在面临着两个字符串顺序选择,他们的 01 数量分别是 one1,one2,ziro1,ziro2,于是考虑两种顺序的情况:

{sum×ziro1+(sum+one1)×ziro2sum×ziro2+(sum+one2)×ziro1

消除公共的 sum×(siro1+ziro2) 后即为:

{one1×ziro2one2×ziro1

按照两两字符串的 01 数量这样排序即可。

2.Desire

算法: 树上差分

给一棵树和若干路径,求选 k 个有交路径的方案数。

我们显然可以直接枚举每个点,计算这个点被覆盖的方案数,但也会遇到算重的问题。于是我们可以考虑对于一个点,计算至少有一条路径以这个点为路径最高点的方案数。

假设有 x 条路径经过这个点,有 y 条路径以该点为最高点,那么方案数就是 (xk)(xyk),然后进行树上差分的处理,再把方案数求和即可。

2023.11.06

1.match

算法:图论

给出一个 n 个点 m 条边的无向图,求最大的边集使得每组边都有一个公共点。

发现点集只会是一个菊花图或者三元环,菊花图处理入度可以解决,三元环的情况当 max(inx,iny)=2 时特判即可。

2.sort

算法: 主席树+中位数

给一个长度为 n 的序列,求下方函数对于序列运行后的 cmpcnt

function sort(a[1...len(a)]):
if len(a) <= 1 then return a
let pivot = a[ceil(len(a) / 2)]
let al, ag = empty sequence
for i in 1...len(a) do
let cmpcnt = cmpcnt + 1
if a[i] < pivot then append a[i] to al
if a[i] > pivot then append a[i] to ag
return sort(al) + [pivot] + sort(ag)

发现每次的操作相当于在一段值域区间上选择了一个点将区间分为两部分,我们将每个数的下表映射到值域上,每次求下标的中位数并在对应值域中的位置进行分割,主席树维护静态区间中位数即可。

3.count

算法: 数论+杜教筛

给出一个数 n,求满足 ab=cda,b,c,d[1,n] 的四元组数。

发现 ab=cd 一条件并不方便处理,所以可以先转换为 ad=cb 的形式:

ans=i=1nj=1n[gcd(i,j)==1]nmax(i,j)

                                 =2×(i=1nφ(i)ni)n2

n 达到了 1011 的级别,直接线性筛显然不可取,需要杜教筛来处理 φ(i) 的前缀和,再套整除分块,预处理后时间复杂度能达到 O(n23)

2023.11.08

1.bracket

算法:括号序列+动态规划dp

给出 n 个括号序列,求选若干个括号序列使其按顺序能拼成合法括号序列的方案数。

可以发现每个括号序列处理后都会剩下形如 )))((( 的序列,我们先预处理出这样剩下的左括号数量和右括号数量,再设 dpi,j 为选到第 i 个序列时左括号为 j 个的方案数,简单转移即可。

2.path

算法: 树链剖分

给出一个 n 个点的带点权树,每次给出一个点对 (u,v),求从 uv 的路径上依次选两点 j,i,最大化 ajai 的值,保证 vu 的祖先。

考虑用树链剖分维护区间最大最小值和答案,但发现 i,j 两点需要在路径上有一定的先后关系是很难处理的问题,于是我们给树链剖分上的线段树记:

struct Tree{
    int minn,maxn,ans;
};

因为保证祖先关系,所以我们树链剖分往上跳链时先后顺序是确定的,所以我们按处理标记让左右和并时有一定顺序,即为:

tr[x].ans=max(max(tr[ls].ans,tr[rs].ans),tr[ls].maxn-tr[rs].minn);
tr[x].maxn=max(tr[ls].maxn,tr[rs].maxn);
tr[x].minn=min(tr[ls].minn,tr[rs].minn);

需要注意的是跳链是分段来跳的,而我们记录答案还要手动将每一段链进行合并处理,就能得到最终答案了。

2023.11.10

1.interval

算法:st

给出一个序列 a,求最长合法区间的左端点满足:区间 gcd 等于区间的某个值。

可以用 st 表维护区间 gcd ,对于每个点二分最小左端点和最大右端点,简单记录去重即可,时间复杂度 O(n log2n)

2.score

算法:二维数点

给出一个 n 个点的树,和 m 个三元组 (x,y,w),表示一条路径先经过 x 并后经过 y 获得 w 的收益(w 为整数),求一条最大收益的路径。

对于每个三元组 (x,y,w) 我们可以分三种情况考虑:

  • xy 是兄弟关系:

很明显,我们只要从 dfs 序为 idxidx+szx1 的点到 idyidy+szy1 的点就能获得 w 的收益。

  • xy 是祖先关系:

我们先讨论 xy 的祖先的情况,我们需要从 x 的上边部分到 y 的子树,但 x 的上边一大块怎么表示呢?其实可以考虑容斥,我们先用倍增跳到既是 x 的儿子又在 xy 这条路径上的点 z,那么上面部分也可以被表示成两段:1idz1idz+szzn,下面子树 x 即为 idxidx+szx1yx 的祖先时同理。

  • xy 是同一个点

我们可以考虑容斥,首先进行全局加,考虑不能经过 x 的路径:祖先到祖先或一个儿子子树到相同的一个儿子子树,因为单点的情况可能会重复,所以需要将点权先记录,最后再对于每个点进行处理,才能使处理的时间复杂度控制到 O(n)

在提炼出若干段区间 (x1,y1)(x2,y2) 后,我们进行一个二维数点的操作,对于横坐标为 x1y1,纵坐标为 x2y2 的矩阵加上贡献,此处横坐标就表示的起点,纵坐标即为终点,将横坐标离线到数轴上,进行一个类似差分的操作,用线段树维护纵坐标的最大贡献即可。

总时间复杂度 O(n log n)

2023.11.12

1.双人博弈

算法:博弈论

给出 n 堆石子,每堆石子 ai 个,Alice 先手至少拿一个石子,两人轮流拿石子的数量必须单调不降,问胜者。

考虑最高数量的石子堆为奇数个时先手必胜,而为偶数个时需要后手拿才必胜,所以只需要判断有没有某种石子有奇数堆即可。

2023.11.13

1.insects

算法:计算几何

给出初始坐标 sx,sy 和每次移动的横纵坐标之比 x,y,并给出两条为从原点到 (a,b) 的直线的镜子,其中一条直线为 x 坐标,求在镜子上的反射次数。

对于反射问题我们可以把镜子按照入射角不断折叠,走直线经过的镜子即为反射的次数,于是我们求出第一次的入射角,算多少个入射角能拼接成 180 即可。

2023.11.14

1.dining

算法:最短路+拓扑序+容斥dp

给出一个 n 个点 m 条边的有向图以及起点 s 和终点 t,求有多少对除起点终点以外无重复点的最短路。

首先因为必须走最短路,所以我们先跑一边最短路,并将所有最短路径的边提出来,这样一来整个图就成为了一个 DAG ,即有向无环图。然后我们可以先处理出这个 DAG 上每个点的拓扑序,尝试用拓扑序来进行动态规划。

可以先记录 fi,j 表示图上从 ij 的路径数量,因为要求路径没有交集,所以考虑在 dp 时枚举两条路径第一次重复的点来进行容斥即可。

2.two

算法: 数论+二分

先给出 n 个形为 f(x)=(xm)2+k 的二次函数,每次进行下列其中一个操作:

  • 再给出一个二次函数

  • 给出 x,t ,删除 f(x)t 的所有二次函数

每次操作后需输出二次函数的个数。

因为顶点式二次函数中 x 的增长对于值的增长是是平方级别的,对于值域 m,k106 函数,每次删除操作只需要考虑对称轴在 x 左右不超过 1000 的二次函数即可。

3.tree

算法: 树论

给定一个无向的树,可以自由地安排访问邻居节点的顺序,求出这颗树字典序最小的先序遍历。

首先一个显然的贪心思路是求出每棵子树的叶子节点的最小值,因为叶子节点优先访问,然后对于每个点,按最小值从小到大遍历邻居即可。一个很棘手的问题是我们如何找到根节点,如果直接枚举根为 O(n2) 复杂度,换根 dp 也不可取。

这时候需要发掘一些性质,我们可以先找出一个特殊的点,即为最小的叶子节点,因为最小叶子节点一定第一个被遍历。然后我们可以发现一个规律,当以最小叶子节点为根的树上,我们往下遍历尝试换根时,根换到邻居中子树的最小值最大且大于当前值的点一定不劣,所以实现了 O(n) 找根,问题迎刃而解。

2023.11.15

1.同余方程

算法:树状数组+数论

给出 n,k,求有多少组 1abcn 满足:

a+b2c3(mod k)

因为 a,b,c 的大小关系的约束,我们可以考虑先枚举 b,这样就能方便表示出 a,c 的取值范围,于是便可以用树状数组维护 c3 对于 k 的同余值,对每个 a 计算即可。

2.部落冲突

算法:广搜

给出 n 个点 m 条边的无向图,以及 q 次操作给出 id,w,d 表示第 id 个部落占领了以 w 为中心向周围扩散 d 次的点。(d10)

因为部落的占领具有覆盖关系,所以我们直接倒序处理操作,并且给每个点记录一个扩散值表示扩散到这个点的操作最多还会往外扩散多少个点,因为扩散值是不断递增的,一旦有一个扩散操作比当前点的最大扩散值小时,无论这个操作如何扩散都没有作用。最后每个点最多只会被遍历 10 次,时间复杂度可接受。

3.图上开花

算法:并查集+图论

给出 n 个点,每个点的权值为 vi,每次我们可以选一个点权为 vi 的点,并新增一个点权为 vi×(vi+1) 的点,求 (s,t) 需要多少次操作才能在所有点权不互质的点对连边之后使 s,t 联通。

因为我们可以构造出形似 vivi(vi+1)vj(vj+1)vj,的路径,因为中途两点一定为偶数,所以答案是不大于 2 的。于是我们思考如何判断答案为 01

答案为 0 的判断并不困难,我们给每个质因数建一个点,把含这个质因数的编号连上这个质因数点,这样点权非互质的点就联通了,相当于直接连边。

答案为 1 的判断也大同小异,我们将每个 vi+1 拿出来处理,用 map 记录下并查集的关系,在判断完 0 后再判断即可。注意 vi+1 的质因数不仅要与 vi 连边,还需要在质因数之间互相连边,才能保证正确性。

4.防御工事

算法:构造+图论

给出树上每个点到其他所有点的距离之和,需要构造出这样的树。

显然距离和最小的点为树的重心,我们考虑从大到小一步一步往上爬,对于一个深度较低的点 u 和他的爸爸 v,容易推出转换关系为 disv=disun+2×sizu,即 uu 上面的点近了一步,离子树远了一步。于是直接用 map 记下深度值,从大往小连边即可。











2023.11.17.NOIP












2023.11.19

1.景点游览

算法:cdq分治

M 所在的城市有 n 个景点,从 1n 编号,由 m 条景点间的单向道路连接。

定义一个非空景点集合 S 是好的当且仅当:

  • S 中的景点编号连续,即 i<k<j,i,jS,则 kS
  • S 中任意景点 u 出发,不能经过若干条道路到达一个景点 vS

需求出有多少个非空景点集合 S 是好的。

这道题第一眼可能以为需要使用 tarjan 来缩点,实际上我们可以转换模型,因为集合中的点的编号一定是连续的,所以我们只需要记录每个点能够到达的编号最小和最大的点 mnmx 即可,这样答案就转换为了:

l=1nr=ln[Min(mnl...mnr)l & Max(mxl...mxr)r]

考虑用 cdq 分治去计算答案,可以预处理 (mid+1,r) 区间满足最大值条件的前缀和,再枚举 (l,mid) 的最大最小值去统计答案即可。

2023.11.20

1.路径大小差

算法:点分治

给出一个带边权的树,求无序二元点对 (u,v) 的数量,要求满足路径最大权与路径最小权之差等于 k

点分治后,我们需要求的即为:(c 为当前重心代表的子树大小)

i=1cj=i+1c[Max(ai,aj)Min(bi,bj)=k]

显然可以先将 ai 进行排序,再用树状数组处理 bi

设当前重心为 rt,注意这里算出来的结果包含两点在 sonrt 中的情况,所以我们需要容斥,对于每个重心的答案即为:

solve(rt)vsonrtsolve(v)

容斥后即为答案。

2023.11.24

1.mex

算法:并查集+图论

每次给出一个二元组,可以对于每个二元组选择一个数放入可重集 S,定义集合 TS 中出现奇数次的数的集合,求每次加入一个二元组后 T 中最大的 mex

首先有一个维护 mex(集合中最小的没出现过的非负整数) 的方法,可以先将范围内的非负整数全部插入到一个降序的优先队列中,每加入一个数在队列中删数求堆顶即可。

这道题每个二元组只能选择一个数,我们考虑将二元组变成一条有向边,只保留入度为奇数的点,最后用并查集维护一下每个连通块的奇偶性即可。

2.slauqe

算法:分类讨论

给出两个只含 1,2 的序列 a,b,要求构造出选若干个位置,使两个序列位置上的数的和各等于其序列总和的一半。

考虑两个序列的总和为 sumasumb,那么我们最后需要位置上的数和为 suma2sumb2,两个序列产生差的话,很显然只有在一个位置上有 (2,1)(1,2) 能产生差,所以我们先将差填好,填完后一组 (2,1)(1,2) 又能组成一组 (3,3),于是现在问题化简成:有 a1b2 以及 c3,求能否组成 x,简单讨论即可。

2023.12.10

1.a

算法:FFT+NTT

给出两个序列 ab,问是否能让 a 位移后(每次将最右侧的数移动到最左边)使得存在一个不超过 k 次的多项式,使得 P(i)aibi (mod p),最小化位移次数。

根据拉格朗日插值法可知,对于一个长度为 n 的序列,至少可以表示为一个 n1 次多项式,所以当 kn1 时,直接输出 0,此后 n,k 同阶。

考虑如何证明一组 ab 能满足条件,我们考虑对于一个多项式 f(x) 降次:

g(x)=f(x)f(x1)

我们聚焦到差分函数的最高次项:

xk(x1)k

显然结果的最高次数为 k1,这样我们就可以用 k 阶差分进行降次,再检验是否有一段长度合法的 aibi 均为 0,至于求位移的次数可以用 kmp 解决,时间复杂度 O(n2),但对于 n105 还是不够。

最后还需要在 k 阶差分时运用 NTT 优化多项式乘法,即可将时间复杂度降为 O(n log n)

2.b

算法:失配树+拓扑排序

给出一个 n 个点 m 条边的 DAG,定义 li 为第 i 条边所指点到 s 的最短路,每条边的价值为 pli。对于每个点需要选择一条边使所有 s 到当前点的路径都经过这条边,同时最大化边的价值。

因为每条边的价值是 pli,这意味着离 s 越远,这条边的价值越小,所以我们需要对于每个点选择离 s 最近的合法边,考虑在拓扑图上标记,因为边的价值取决于点,所以我们先把每条边映射到一个点上,记 fxx 最优的合法边对应的点,考虑如何转移:

  • 如果当前点入度等于 1,可以从上个点直接转移

  • 如果当前点入度大于 1,需要判断所有入边是否来自同一条路径

最后对于每个初始入度为 1 的点加上其 fx 的价值即可。

3.c

算法:状态压缩dp

给出一个 N×M 的棋盘,初始所有点都朝下,我们可以至多选两个 X×Y 的矩阵,可以让矩阵内任意棋子翻转,问最后棋盘有多少种情况。

直接枚举所选矩阵很容易算重,所以我们考虑枚举答案。

我们可以枚举一个包含所有朝上棋子的最小矩阵,这个矩阵里的点需要满足:

  • 矩阵的最左边和最右边有点

  • 矩阵的最上边和最下边有点

  • 矩阵的所有点一定都在左上角和右下角的所选矩阵(或左下角和右上角)

我们将这六种情况记录成一个六位二进制数,发现最后选到的点前四位或起来并且后两位与起来为 111101111110111111 满足条件,于是我们算出最小矩阵中每个点的状态,再记 dpi,S 表示考虑到第 i 种状态,当前状态为 S 的方案数,转移:

dpi,S×(2cnti1)dpi+1,S|i

dpi,Sdpi+1,S

最后将枚举的大小为 H×W 的矩阵方案数乘上它在原棋盘中出现的次数求和即可。

2023.12.11

1.混乱

算法:分块

2.七管荧光灯

算法:博弈论+数位dp

给出三个值域,求有多少组 a[l1,r1]b[l2,r2]c[l3,r3] 满足 abc=0

因为亦或和为 0,可以在二进制位上进行数位 dp,设 fpos,0/1,0/1,0/1,0/1,0/1,0/1 表示在二进制第 pos 位时,a,b,c 是否到达上下界,每次转移只需要枚举 a,b,即可将 c 表示为 ab,记忆化搜索即可。

3.字符串

算法:线段树+哈希+并查集启发式合并

有一个字符集为 [1,10100] 的未知字符串,每次进行如下任意操作:

  • 给出一段区间 S[l,r],表示将 S[l,r] 为回文串加入条件

  • 询问 S[l1,r1]S[l2,r2] 是否相同或不确定。

这道题初始我们每个点的颜色各不相同,考虑操作一带来的影响,对于 i[0,mid] 使 l+iri 相同,首先考虑到可以用并查集维护,但直接枚举显然会超时,又想到并查集的合并最多有 n1 次,呈树状,所以我们用线段树维护一个前后缀哈希,每次二分找到不相同的两端并进行启发式合并。至于查询,直接判断两段的哈希是否相同即可,时间复杂度 O((n+m) log2 n)

2023.12.14

1.tmp

算法:网格图+树状数组

2.弦论

算法:状压dp+贝尔数

3.原题

算法:概率+期望+二项式定理

有一个初始值为 0 的序列 a 和变量 x,每次进行以下三个操作中的一种:

  • l,r,w 使 max(w,ai)ai (lir)

  • l,r,w 使 min(w,ai)ai (lir)

  • l,r 使 ai+xx (lir)

每次操作完都会输出 x

给出 n,m,q,表示共 q 次操作, 1lrn0w<m

求所有 ((2×m+1)n×(n+1)2)q 的输出之和,对 109+7 取模。

直接推操作后的答案是困难的,所以考虑求每个答案 x 的期望次数。又发现每个位置是独立的,考虑分开求解。

我们设:

Pt,i 表示进行 t 次修改之后一个数字变成 i 的概率。

Et 表示一个值被修改 t 次之后的期望。

我们考虑如何求 Pt,i,发现可以分两种情况讨论:由上个 j 变为 i 或 上个 i 经过操作后不变,即:

Pt,i=m×Pt1,i+j=0m1Pt1,j2m=12m+Pt1,i2

最后通过求和的通项公式可以化简出:

Pt,i=1m1m2t

pi 为一个操作包含 i 的概率,显然:

pi=2i(ni+1)n(n+1)

接下来我们需要求经过 t 次修改第 i 个位置的期望 ht,i:(二项式定理来咯)

ht,i=j=0t(tj)pij(1pi)tjE(t)

=m12(j=0t(tj)pij(1pi)tjj=0t(tj)(pi2)j(1pi)tj)

=m12(1(pi2+(1pi))t)

=m12(1(1pi2)t)

为方便推导,我们再记 Pi=(1pi2)

最后就是要求经过 t 次操作后第 i 个位置的期望 gt,i

gt,i=j=0t(tj)(2m2m+1)j(12m+1)tjhj,i

=m12(1(2mPi+12m+1)t)

最后只需要考虑每个位置 i 对于答案的贡献 fi

fi=pi2m+1m12j=1q(1(2mPi+12m+1)j1)

答案即为 fi,时间复杂度 O(N log Q)

2023.12.17

1.温泉

算法:分块

2.摆放石子

算法:找规律

3.游戏

算法:SG函数

2023.12.21

2.森林

算法:权值线段树

3.队伍

算法:动态规划+FFT

2023.12.24

1.晚霞

算法:分块+虚树

2.矩阵

算法:线性变换+矩阵乘法

3.相似序列

算法:主席树+哈希

2023.12.27

1.冒泡排序

算法:线段树

2.上升序列

算法:NTT

3.地铁换乘

算法:图论+并查集

2023.12.30

1.数字重组

算法:整数拆分

2.摆放鞋子

算法:二分图匹配

3.强连通

算法:缩点

posted @   Chax  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示