NOIP 历年题集
2015
D1 T1 神奇的幻方
简单模拟。
D1 T2 信息传递
可以发现,我们要求的即为该有向图的 最小环,观察该图,是一个内向基环树,我们可以直接 dfs 找环即可。
D1 T3 斗地主
好像是个爆搜剪枝?不想写。
D2 T1 跳石头
显然可以二分一个 \(mid\),然后我们贪心的选择删除距离小于 \(mid\) 的点,然后判断是否 \(\leq m\) 即可。
复杂度 \(\mathcal{O}(n\log{V})\)。
D2 T2 子串
考虑动态规划,设 \(f_{i,j,k}\) 表示 \(A\) 前 \(i\) 个字符串取出了 \(j\) 个字符串,匹配到 \(B\) 的第 \(k\) 个字符,且 \(A\) 的第 \(i\) 个字符一定匹配,即 \(a_i = b_k\)。
则有转移方程,若 \(a_i = b_k\),则 \(f_{i,j,k} = f_{i-1,j,k-1} + \sum\limits_{w = 0}^{i-2}f_{i-1,j-1,w}\),答案即为 \(\sum\limits_{i=1}^{n} f_{i,k,m}\)。
前缀和优化一下则复杂度为 \(\mathcal{O}(nkm)\),内存需要滚一下。
D2 T3 运输计划
蛮好的题。
首先有二分一个 \(mid\),预处理出 \(m\) 条道路的长度,可以 倍增/树剖 求,对于长度 \(d_i > mid\) 的道路,我们需要找到其路径上的一条边进行虫洞修改,所以我们对于每条边,若其覆盖到了所有 \(d_i > mid\) 的道路,且 \(max_d - w_k \leq mid\),则可以满足,其中 \(w_k\) 为当前边的权值。
而对于如何覆盖路径,可以暴力树剖,更简单的可以树上差分。
复杂度 \(\mathcal{O}(n\log{n} + n\log{V})\)。
2016
D1 T1 玩具谜题
一点分讨。
D1 T2 天天爱跑步
豪题。
对于树上一条路径 \(s \rightarrow t\),对于在 \(x\) 节点的观察员,我们考虑其可以观察到该路径上的人,需要满足的条件。
- 若 \(x\) 在 \(s \rightarrow \text{LCA(s,t)}\) 上,则需要满足 \(dep_x + c_x = dep_s\)。
- 若 \(x\) 在 \(\text{LCA(s,t)} \rightarrow t\) 上,需要满足 \(2dep_{\text{LCA(s,t)}} + c_x = dep_s + dep_x \Rightarrow c_x - dep_x = dep_s - 2dep_{\text{LCA(s,t)}}\).
- 否则 \(x\) 不在 \(s \rightarrow t\) 上,一定不成立。
所以对于一条路径 \((s,t)\),可能满足的节点是两条不同的路径,即 \(s \rightarrow \text{LCA(s,t)}\) 与 \(\text{LCA(s,t)} \rightarrow t\),所以我们可以 树上差分 求贡献,具体的。
- 我们在 \(s\) 节点上存 \((dep_s,1)\),并在 \(\text{LCA(s,t)}\) 上存 \((dep_s,-1)\)。
- 我们在 \(t\) 节点上存 \((dep_s - 2dep_{\text{LCA(s,t)}},1)\),并在 \(fa_{\text{LCA(s,t)}}\) 上存 \((dep_s - 2dep_{\text{LCA(s,t)}},-1)\),不能多减贡献。
当扫描到该节点时用两个桶分别记录两种贡献,对于节点 \(x\),我们所要的答案是其子树内的贡献,所以我们可以类似差分求出子树内贡献,即进入节点时先减去,出节点时在加上。
对于统计答案,即为 \(cnt1[dep_x + c_x] + cnt2[c_x - dep_x]\)。
桶可以 map 也可以数组,复杂度瓶颈在求 \(LCA\),总复杂度 \(\mathcal{O}(n\log{n})\)。
D1 T3
D2 T1 组合数问题
可以直接递推求组合数,再二维前缀和下即可。
D2 T2 蚯蚓
即每次取出最大点后全局加,显然可以简单优先队列维护,不过复杂度是 \(\mathcal{O}(n\log{n} + m\log{n})\),85 pts。
我们观察下性质,\(p\) 是常数,这意味着若我们从大往小取,则 \(\lfloor px \rfloor\) 与 \(x - \lfloor px \rfloor\) 也是单调递减的。
所以我们可以维护三个队列,表示原队列,\(\lfloor px \rfloor\) 队列与\(x - \lfloor px \rfloor\)队列,我们可以先对于原队列排个序,则任意时刻,三个队列始终都是单调递减的,我们只需比较三个队头即可。
对于全局加,可以等价于单点减,维护一个全局标记即可。
复杂度 \(\mathcal{O}(n\log{n} + m)\),貌似有些精度误差?
D2 T3 愤怒的小鸟
由数学知识可得,三点可以确定一个二次函数,题目中有固定点 \((0,0)\),所以任意两个点可以确定一条抛物线。
\(n\) 很小,考虑状态压缩,我们令 \(p_{i,j}\) 表示以 \(i,j\) 两个点确定的抛物线所经过的点的集合,可以 \(\mathcal{O}(n^3)\) 求,解个方程即可。
设 \(f_{now}\) 表示当前消灭了 \(now\) 状态的小猪所需最少数量,则对于 \(now\),我们可以枚举两点有 \(f_{now} + 1 \rightarrow f_{now \ | \ p_{i,j}}\),这样复杂度为 \(\mathcal{O}(2^nn^2)\)。
复杂度很满,考虑优化,对于状态 \(now\),我们不在乎其顺序,令 \(x\) 为 \(now\) 最低位为 \(0\) 的位数,若我们不转移 \(x\),则之后一定还需转移 \(x\),所以我们可以优化掉这一步,只从 \(x\) 转移,这样复杂度优化到了 \(\mathcal{O}(2^nn)\)。
对于求 \(x\),可以用 __builtin_ctz(~now)
,这个函数是求二进制尾部存在多少个 \(0\),我们取反即可得到尾部 \(1\) 的个数,\(+1\) 即为最低位 \(0\) 的位数。
2017
D1 T1 小凯的疑惑
答案即为 \(a \times b - a - b\)。
等我补证明。
D1 T2 时间复杂度
脑瘫模拟,不会。
久远代码,我自己都不知道在写什么。
D1 T3 逛公园
\(K \leq 50\) 的提示性很强,设 \(d_x\) 表示从 \(1\) 到 \(x\) 的最短路径,考虑 \(f_{x,j}\) 表示从 \(1\) 到 \(x\) 节点路径长度为 \(d_x + j\) 的个数,转移对于边 \((x,y)\),有 \(f_{y,j} \gets f_{x,d_y + j - w - d_x}, \ (d_y + j - w - d_x) \in [0,K]\),可以记忆化搜索。
对于 \(-1\) 可以发现当且仅当存在 \(0\) 环,我们可以在记搜中记录搜过的状态,若在记搜过程中搜到过经过的状态,则一定存在 \(0\) 环。
复杂度 \(\mathcal{O}(m\log{n} + nK)\)。
D2 T1 奶酪
若两个圆相交,则可以合并,可以用并查集维护,复杂度 \(\mathcal{O}(n^2\log{n})\)。
最后首尾判断是否联通即可。
D2 T2 宝藏
\(n\) 很小,考虑状压,可以发现每个宝藏屋有一个 \(L\) 的贡献,所以我们考虑分层,设 \(f_{i,now}\) 表示当前选了 \(now\) 状态的宝藏屋,且层数都 \(\leq i\) 的最小代价。
则我们可以枚举 \(now\) 的子集 \(p\),有 \(f_{i,now} = \min f_{i-1,p} + w_{p \rightarrow now}\),其中 \(w_{p \rightarrow now}\) 表示从 \(p\) 状态转移到 \(now\) 状态的最小代价,可以枚举节点 \(\mathcal{O}(n^2)\) 求,枚举子集的复杂度为 \(\mathcal{3^n}\)。
总复杂度 \(\mathcal{O}(3^nn^2)\),复杂度很满,但可过。
代码,\(\mathcal{O}(3^nn^2)\) 的。
考虑优化,令 \(w_{p,q}\) 表示上一层状态为 \(p\) 下一层状态为 \(q\) 时的最小代价,有 \(w_{p,q} = \min w_{p,q - lowbit(q)} + s_{x,p}\),其中 \(x\) 表示 \(lowbit(q)\) 的位数,\(s_{x,p}\) 表示 \(x\) 与状态 \(p\) 中所有点最小距离,全部预处理出来,复杂度 \(\mathcal{O}(3^nn + 2^nn)\)。
没写代码。
D2 T3 列队
?
2018
D1 T1 铺设道路
区间减相当于差分数组的单点修改,则我们求出差分数组,找出最大修改值即可。
D1 T2 货币系统
我们发现,对于价值为 \(w\) 的货币,当且仅当其可以被其他货币组成,可以删去该货币,完全背包即可。
复杂度 \(\mathcal{O}(TnV)\)。
D1 T3 赛道修建
首先可以二分 \(mid\),我们可以贪心的找出长度 \(\geq mid\) 的路径,对于点 \(x\),其儿子节点 \(y\),我们上传其最长不满足的路径,我们可以维护一个 multiset,枚举未满足的路径,二分最小的另一条路径,满足 \(len1 + len2 \geq mid\),最后判断满足条件的路径是否 \(\geq m\) 即可。
复杂度 \(\mathcal{O}(n\log{n})\)。
D2 T1 旅行
找环,暴力断环即可,复杂度 \(\mathcal{O}(n^2)\)。
不会 \(\mathcal{O}(n)\)。
D2 T2 填数游戏
分讨你妈。
D2 T3 保卫王国
DDP,历史第一。
不看修改,该问题是树上最小全覆盖集。
看修改,好麻烦。
2020
T1 排水系统
忘了,不想看。
T2 字符串匹配
首先,我们可以设 \(cnt\) 表示字符串 \(S\) 的后缀奇数次字符数量。此时若存在 \((AB)^i\) 可以与 \(S\) 前 \(k\) 个字符相等,我们就可以统计 \(\leq cnt_{k+1}\) 的 \(A\) 的数量。
这样我们可以枚举 \(AB\) 这个整体,枚举 \(i\),然后可以 Hash 判断,这是调和级数的。
对于统计 \(\leq cnt_{k+1}\) 的 \(A\),我们可以暴力 BIT,总复杂度 \(\mathcal{O}(n\log{n}\log{26})\),哈我当时以为是两个 log。
可以发现值域很小,可以直接统计,复杂度 \(\mathcal{O}(n\log{n} + 26n)\),然后我莫名想到根号,根号平衡可做到 \(\mathcal{O}(n\log{n} + \sqrt{26}n)?\) 我可能很神经。
有更优的做法,我不会。
代码,\(\mathcal{O}(n\log{n}\log{26})\) 的,能过。
T3 移球游戏
我们考虑如何将一种颜色移到一个柱子上,我们考虑两步,设当前我们要的颜色为 \(c\),对于每一个柱子,我们尝试将所有颜色为 \(c\) 的球移到最上方,设有 \(cnt\) 个。
- 首先我们选定另一个非空柱子,我们移动顶部 \(cnt\) 个到空柱子,然后对于当前柱子,若其颜色为 \(c\),则将其移动到另一个非空柱子上,否则将其移动到空柱子上。
- 然后我们将移到空柱子上的当前柱子先还原,然后还原颜色为 \(c\) 的球,最后还原空柱子剩下的球。
这样我们将所有颜色为 \(c\) 的球移动到了顶端,就可以将所有颜色为 \(c\) 的球存放到一个柱子上,这样我们就完成了一个柱子的任务。
重复 \(n\) 次即可,总操作次数 \(\mathcal{O}(n^2m)\),70 pts,尝试卡了卡次数,未尝进展。
如何优化?我们做了 \(n\) 次上述操作,尝试消减,我们可以分治,每次尝试将颜色在 \([l,mid]\) 的球放到左边,在 \([mid+1,r]\) 的球放到右边,这样我们整体处理,只需要做 \(\log{n}\) 次,总操作次数 \(\mathcal{O}(nm\log{n})\)。
T4 微信步数
对于每个维度,维护走出步数区间,直接统计答案,不会。
2021
T1
T2
T3
T4
2022
T1 种花
后缀和,下缀和随便弄弄,复杂度 \(\mathcal{O}(n^2)\)。
T2 喵了个喵
我们首先考虑 \(k = 2n - 2 = 2(n - 1)\) 的情况.
当我们将每两种卡牌放到1个栈内,最后会多出1个额外的栈,我们就可以把这个栈作为辅助栈,进行操作2,再考虑将第 \(2i-1\) 和第 \(2i\) 种纸牌放到第 \(i\) 个栈内,则我们就可以把第 \(n\) 个栈作为辅助栈。
所以我们就可以得到以下的解法,当我们将卡牌 \(x\) 放到栈 \(st\) 内时,考虑每种情况得到:
- 当栈 \(st\) 为空时,直接将 \(x\) 入栈。
- 当栈 \(st\) 有一个元素,且这个元素与 \(x\) 相等,则直接将 \(x\) 入栈,并消掉。
- 当栈 \(st\) 有一个元素,但这个元素与 \(x\) 不相等,则将 \(x\) 入栈。
- 当栈 \(st\),且栈顶元素与 \(x\) 相等,则直接将 \(x\) 入栈,并消掉。
- 当栈 \(st\) 有两个有两个元素元素,但栈顶元素与 \(x\) 不相等,则栈底元素一定和 \(x\) 相等,这时将 \(x\) 入到辅助栈内,用操作 \(2\) 消掉。
这样就得到了 15 pts。
接下来我们考虑 \(k = 2n - 1\) 的情况:
有很明显的一种写法,对于数字 \(x \leq 2n - 2\) 时我们依旧按照上一个方法,但当遇到数字 \(x = 2n - 1\) 时较为繁杂,此时的局面结构不定,所以我们换另一种方法
我们抛开栈 \(i\) 和数字 \(2i-1\) 和 \(2i\) 的绑定关系,采用先来后到的方式,尽可能的按照 \(k = 2n -2\) 的策略,具体是:
- 如果遇到的数字 \(x\) 未出现在栈中,则就把 \(x\) 入到一个还没满 \(2\) 个元素的栈,如果有 \(n-1\)个栈都满了就代表这种策略走不下去了 (因为我们必须留一个辅助栈)。
- 如果 \(x\) 已经出现在栈中了,则按照上一个方法消掉 \(x\)。
直到走不下去了,此时一定是有 \(n-1\) 个栈全部都有两个元素,且每个元素各不相同,剩下一个就是将要填的数 \(P\) (与栈内任何一个元素都不相同)。
此时我们考虑P的放置方法:直接把 \(P\) 放到辅助栈显然是错误的,假如 \(P\) 后面跟了一个被压在栈底的数字,那么这两个数字可能很难再抵消。
我们考虑离线:也就是说,我们开始预知这个数后面都是什么数,依据后面数的情况,我们考虑 \(P\) 放在哪里,考虑紧跟在 \(P\) 后方最长的一串数,满足这些数都在栈顶。
如果不考虑当前 \(P\) 的去向,那么直接将这些数放到对应的栈,和栈顶相消即可,如果再来就仍然放置原来的栈顶。比如 \(5\) 是某个栈的栈顶,如果 \(P\) 后面来了个\(5\) 就让它和 \(5\) 相消,如果再来一个 \(5\) 就放在原来的栈顶,如果再来就继续相消。
因此发现,如果 \(P\) 后面第一个不在栈顶上的数是 \(P\),那么我们就把当前的 \(P\) 放入辅助栈,然后接下来的数自由相消,最后在第二个 \(P\)来时,将它也放入辅助栈,栈顶相消即可。
现在讨论 \(P\) 后面第一个不在栈顶上的数是一个栈底 \(X\) 的情况。我们记其对应栈为 \(S\)。
此时未来的数列是 \((P,⋯,X)\),其中 \(⋯\) 都是某个栈的栈顶。我们讨论 \(S\) 当前的栈顶 \(Y\)。
- 如果未来数列的 \(P\) 和 \(X\) 之间,\(Y\) 出现了奇数次:把 \(P\) 放入辅助栈,接下来一直自由相消,直到将处理 \(X\) 为止。由于 \(Y\) 的数量是奇数,加上一开始的一个 \(Y\),此时 \(Y\) 一定被消除光,现在 \(S\) 的栈顶变成 \(X\),将 \(X\) 放入 \(S\) 栈顶相消,\(S\) 变成了空栈。
- 如果 \(Y\) 出现了偶数次:把 \(P\) 放入 \(S\),未来所有不为 \(Y\) 的栈顶自由相消,对于 \(Y\) 我们将其全部放入辅助栈相消,直到将处理X为止。由于 \(Y\) 的数量是偶数,这些 \(Y\) 一定都被消除光,辅助栈仍为空,我们将 \(X\) 放入辅助栈,和 \(S\) 栈底相消,辅助栈仍为空。
经过上述操作后始终会满足存在辅助栈,栈内最多只有两种元素,所有数在栈中只出现一次,所以我们可以一直执行。
复杂度 \(\mathcal{O}(n)\)。
T3 建造军营
对于一个环内的任意两条军营,切断任意哪一条路径都不会影响两城市的联通,即环内的点时没有影响的,我们可以考虑缩点成一棵树,只有树边需要考虑。
设 \(f_{x,1}\) 表示 \(x\) 子树内建造了军营,且向 \(x\) 的所有道路都看守的方案数,\(f_{x,0}\) 表示 \(x\) 子树内不建造军营的方案数。
初始的有 \(f_{x,0} = 2^{s_x},f_{x,1} = 2^{s_x + size_x} - f_{x,0}\),其中 \(s_x\) 表示缩点后 \(x\) 点内边的数量,\(size_x\) 表示节点的数量。
则对于 \(x\) 新加入的节点 \(y\),有
- \(f_{x,0} \gets f_{x,0} \times 2 \times f_{y,0}\)
- \(f_{x,1} \gets f_{x,1} \times 2 \times f_{y,0} + f_{x,1} \times f_{y,1} + f_{x,0} \times f_{y,1}\)
对于节点 \(x\),若 \(x = 1\),对答案贡献即为 \(f_{x,1}\),否则,在 \(x\) 子树外的边可以任意选,并强制 \(x \rightarrow fa\) 这条边不选,贡献为 \(f_{x,1} \times 2^{m - s_x - 1}\)。
复杂度 \(\mathcal{O}(n)\)。
T4 比赛
扫描线,可以单调栈维护,该为区间加,对于询问 \((l,r)\) 我们将其挂到。
然后我们需要维护的即为 \(c_i \times d_i\) 的和的区间和。
对于数据有 \((len,sc,cd,cd,scd)\) 分别表示区间长度,历史 \(c\) 和,历史 \(d\) 和,\(cd\) 和,\(cd\) 的历史和。
对于标记有 \((lc,ld,t,hc,hd,s)\) 分别表示 \(c\) 标记,\(d\) 标记,时间区间 \(t\),历史 \(c\) 标记和,历史 \(d\) 标记和,历史标记 \(cd\) 和。
对于标记合并有 $
2023
·