NOIP模拟赛题解

T1:

[JOI 2020 Final] JJOOII 2

签到题。

首先不难想到枚举第一个 N 所在的位置,然后从此开始往后找到恰好 K K K 个 N,再从此开始找恰好 K K K 个 O,再找 I。这样做最终一定能够找到最优解,但复杂度太大了。所以考虑记录‘N’、‘O’、‘I’ 的所有所在位置,开 3 3 3 个数组分别维护,由于我们枚举的位置的单调性,这个复杂度是做到可以线性的。

如果没想到的话也有另一种做法,二分+前缀和。具体地,维护这三个字符出现次数的前缀和,然后依然是枚举第一个 N 所在位置,二分求出后面第 K K K 个 N,再找‘O’、‘I’,跟上述做法思路差不多,实现方式略有差别。时间复杂度 O ( n l o g n ) O(n_{}logn) O(nlogn)

T2:

「EVOI-RD2」童年

加强版

Subtask 3:统计所有大于 0 0 0 a i a_i ai 即可,注意特判 m − ∣ a 1 ∣ < 0 m-|a_1|<0 ma1<0 的情况。

Subtask 4:从 1 1 1 走到 n n n,直到不能走,路上统计最大值即可,时间复杂度线性。

再说正解,比较 naive 的想法是统计子树中的点权和,然而也很显然这是错的。

考虑什么时候才会进入一个节点及其子树,必然是能够赚到钱,由于有一些店铺需要买东西,需要一开始就有一定量的钱,所以设 f u f_u fu 表示进入 u u u 节点及其子树并最终赚到的最少需要的钱数。 求出它后我们从根节点开始 bfs,用优先队列维护当前 f f f 值最小的点 v v v,每次取出队头,加上 a v a_v av,进行更新,直到队空或发现当前钱数小于 f v f_v fv

关键就是求出 f f f 数组。
对于 u u u 结点,假如说 a u ≥ 0 a_u\ge 0 au0,那么 f u = 0 f_u=0 fu=0
否则将 u u u 所有子节点加入优先队列,并记当前总收入 s u m sum sum,初始为 0 0 0。每次取出 f v f_v fv 最小的节点 v v v,若 s u m > f u sum>f_u sum>fu,意味着已经可以赚到,就可以退出循环了。
否则若 s u m < f v sum<f_v sum<fv,则还多需要 f v − s u m f_v-sum fvsum 的钱数,令 f u ← f u + f v − s u m f_u\gets f_u+f_v-sum fufu+fvsum,然后令 s u m ← f v sum\gets f_v sumfv,最后将 s u m sum sum 再加上节点 v v v 的点权,将 v v v 子节点入队,就可以求出 f f f 了。
注意最后退出循环后还要再判断一次 s u m > f u sum>f_u sum>fu,否则 f u ← I N F f_u\gets INF fuINF,进入 u u u 子树无法赚到钱。

注意队列的清空,时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值