NOIP合集
感觉最近状态相当差劲。
P7960 [NOIP2021] 报数
依题意枚举特殊数字的倍数即可。
P7961 [NOIP2021] 数列
直接无脑状压可以拿到 \(\operatorname{50pts}\) 。
实际上可以不关注全局的状态而只关注进位,通过维护当前位置为 \(1\) 的个数和进位进而得到全局中位置为 \(1\) 的个数。
设 \(f_{i,j,k,p}\) 为枚举到了 \(a_i\),已经放了 \(j\) 个数,当前有 \(k\) 为 \(1\),进位为 \(p\) 的答案,转移为:
最后统计 \(k+popcount(p)\le K\) 的答案即可
注意状态的设定和转化,注意简化计算答案的过程。
P7962 [NOIP2021] 方差
模拟退火有 \(\operatorname{40pts}\) ,看出正解性质的模拟退火可以切掉。
我超我连交换差分都没看出来。
题解说答案最优时序列的差分是满足单谷性质的,然而并没有看懂,\(\operatorname{sandom}\) 说用邻项微扰可以证来着🤔...
考虑 \(\operatorname{dp}\) ,首先求出差分数组并将其排序,从小到大枚举差分放到单谷左侧还是单谷右侧。设 \(f_{i,x}\) 为枚举到了第 \(i\) 个差分 \(d\),当前的和为 \(x\) 时的最小平方和,转移为:
最后根据每个状态计算答案,同时在转移过程中要注意枚举上界的问题。
P7113 [NOIP2020] 排水系统
拓扑排序加高精,开int128
也能过。
P7114 [NOIP2020] 字符串匹配
人类在我这里成功退化了(!🐒
也不知道咋想的把一个能 \(O( n\log n)\) 做的玩意儿硬生生干成了 \(O( n\sqrt n)\)。
考虑枚举长度和长度的倍数,同时统计真前缀中同一奇数次出现字母个数的子串数前缀和。对于每一次枚举先用哈希判一下是否为满足题目所给出的条件,再计算该状态下小于后缀奇数次出现字母个数的子串有多少,稍微优化一下常数可以通过。
不会 \(z\) 函数。
P5020 [NOIP2018 提高组] 货币系统
容易知道当一个大的币值能被集合中若干较小的数拼出来的时候,这个币值就是无用的。
初始化 \(ans=n\) ,将币值从小到大排序,依次跑完全背包。每到一个币值先判断这个币值是否已经被拼了出来,若是则将 \(ans\) 减 \(1\) ,否则用该币值进行背包。
P5021 [NOIP2018 提高组] 赛道修建
二分一个赛道长度最小值 \(mid\),为了满足条件应该在树中找 \(\ge m\) 条以 \(mid\)为最小值的链。在某颗以 \(u\) 节点为根的子树中,出现一条长度 \(\ge mid\) 的链有两种情况:
-
某一子节点 \(v\) 到叶节点的链的长度 \(val_v \ge mid\)
-
两个子节点所代表的链拼起来的长度 \(val_a+val_b \ge mid\)
前一个可以直接加到答案贡献里,重要的实际上是第二种。
一种贪心做法是将所有当前传上来的所有长度 \(< mid\) 的链放进一个multiset
里,每次对集合中长度最小的链二分查找能与之拼成 \(\ge mid\) 长度的链的另一条链,然后将这两条链删去继续查找。最后将multiset
内剩下的最大链长传给父节点。
因为对于可以拼接的两条链,让其中一个的链成增大并不会使答案变得更优,因为这两条链的贡献最多为 \(1\),同时尽量选较短的去拼接也能让长链有机会去传递给父节点对答案产生贡献,所以该贪心是正确的。
复杂度为 \(O(n\log ^2 n)\)。
P3960 [NOIP2017 提高组] 列队
平衡树做法见这篇boke
被 \(\operatorname{crs}\) 的 \(90\) 行平衡树爆踩,但其实是一样的。
树状数组的话可以将询问离线下来,对每一行单独进行操作。其他位置的变动也可以开vector
维护。
P3953 [NOIP2017 提高组] 逛公园
我超什么神仙 \(\operatorname{dp}\) 😰。
设 \(f_{u,j}\) 为到 \(u\) 节点与最短路 \(dis_u\) 的偏移量为 \(j\) 的方案数,设边 \((u,v)\) 的权值为 \(w\) ,则转移有:
枚举顺序的话可以先跑一遍最短路然后拓扑排序。
但有个比较麻烦的是 \(0\) 环,这玩意的贡献是无限的,如果觉得写拓扑比较麻烦,建反图然后记忆化搜索可能会好写一些。设 \(vis_{u,k}\) 表示当前状态是否仍在递归,若在递归过程中遇到了 \(vis_{u,k}\) 为真的情况则说明有 \(0\) 环。
多测不清空——
P2827 [NOIP2016 提高组] 蚯蚓
😇
注意到给其他所有蚯蚓增加长度相当于自己减少长度,在过程中记录整个序列的变化量可以求得某一个数的真实值,结合优先队列可以拿到 \(\operatorname{85pts}\) 。
但正解显然是线性的,能证明首先切的蚯蚓的两部分的长度一定会大于等于后来切的蚯蚓长度,所以可以直接将这两部分放在队列中。每次判断取优先队列的队头还是这两个切过的队列的队头即可。
复杂度 \(O(t)\) 。
P1084 [NOIP2012 提高组] 疫情控制
我超什么神仙 \(\operatorname{二分}\) 😰。
显然把答案范围内让所有军队尽量往根节点方向上走会很优。
二分一个值 \(mid\) ,让所有军队尽量往根节点方向上走,然后再考虑分配给根节点的各个子节点。
第一个部分可以用倍增解决,当然如果走不动了要在这个点打标记,如果走到了根节点的子节点之后还能走,把能移动的距离放到一个multiset
中,并在这个子节点上取可移动距离的最小值 \(up_v\)(至于为什么看第二部分)。判断某棵子树合法的条件是这棵子树的所有子树都合法(都打上了标记),最后只对不合法的子树分配军队。
第二个部分,设 \((1,v)\) 的边权为 \(w\),对两种情况进行讨论:
-
\(w>up_v\) ,这时把这个子树的军队退回去会比选可移动距离更大的优。
-
否则,二分找到最小的可以到达这个子树的军队。
(因为可移动距离更大的放到距离较小的子节点是不优的,杀鸡为何用牛刀)
然后这题大概就做完了,果然自己思路和实现能力都很欠缺啊...
upd:不要问为什么没有棋局啊移球游戏啊微信步数啊什么的。
要问答案就是 \(\huge因为我被开除了人籍\)