HNOI 2018 Day1 题解
Luogu4424 [HNOI/AHOI2018]寻宝游戏
给出一个含有 \(n\) 个数的二进制数组 \(a_1,a_2,...,a_n\),和 \(q\) 个长度也为 \(m\) 的二进制数 \(r_1,r_2,...,r_q\)。
在保持数组 \(a_1,a_2,...,a_n\) 的元素顺序不变下,你可以在它们之间插入 \(\land\)(按位与运算)或者 \(\lor\)(按位或运算)。
你需要插入 \(n\) 个运算符,相邻两个数之前恰好一个,在第一个数的左边还有一个。如果我们在第一个运算符的左边补入一个 0,这就形成了一个运算式,我们可以计算它的值。与往常一样,运算顺序是从左到右。现在,对 \(r_1,r_2,...,r_q\) 中的每一个值 \(r_i\),分别计算出有多少种方法填入这 \(n\) 个计算符,使的这个运算式的值是 \(r_i\) ,模 \(1000000007\) 。
\(n \le 1000, m \le 5000, q \le 1000\)
排序
位运算
结论
考试的时候,想到了从后往前,对于 \(\wedge\),如果是该位是 \(0\),那么就会全变成 \(0\),\(\vee\) 类似,然后就打了一个记搜,开了 O2 跑得飞快 (对于前 70 分可以 0.5s 完美通过),然后考到一半发现没有 O2,然后就 * 10 倍常数,就是 5 秒了,卡了卡常,成了 2s,但是时限 1s,直接裂开。
其实正解就是继续往下想,但是我想不到。
这个限制很像字典序,于是我们可以倒着考虑,对于某一位,我们把 \(n\) 个数的这一位拆下来,拼成一个长为 \(n\) 的 \(01\) 串 \(a\)(后面的作为高位),然后我们操作串为 \(b\)(同样,后面的作为高位,如果为 \(\vee\),那么就是 \(0\),否则为 \(1\)),然后我们进行字典序比较,在对于第一个位置 \(i, a_i \neq b_i\),如果 \(a_i\) 为 \(0\),那么这一位就是 \(0\) 了,否则就是 \(1\) 了,于是我们最后的操作串的字典序 < \(a\) 那么就是 \(1\),否则为 \(0\)。
于是我们对于每条询问,就会有 \(m\) 个关于操作串的限制,我们只要确定左右端点,然后就是这里面的串都能选了。
代码
Luogu4425 [HNOI/AHOI2018]转盘
一个转盘上有摆成一圈的 \(n\) 个物品(编号 \(1\sim n\)),其中的 \(i\) 个物品会在 \(T_i\) 时刻出现。
在 \(0\) 时刻时,小 G 可以任选 \(n\) 个物品中的一个,我们将其编号为 \(s_0\)。并且如果 \(i\) 时刻选择了物品 \(s_i\),那么 \(i+1\) 时刻可以继续选择当前物品或选择下一个物品。当 \(s_i\) 为 \(n\) 时,下一个物品为物品 \(1\),否则为物品 \(s_{i}+1\)。在每一时刻(包括 0 时刻),如果小 G 选择的物品已经出现了,那么小 G 将会标记它。小 H 想知道,在物品选择的最优策略下,小 G 什么时候能标记所有物品?
但麻烦的是,物品的出现时间会不时修改。我们将其描述为 \(m\) 次修改,每次修改将改变其中一个物品的出现时间。每次修改后,你也需求出当前局面的答案。对于其中部分测试点,强制在线。
\(n \le 10^5\)
数据结构
线段树
单调栈
题解
看到循环走,几乎弃疗了,然后啥也不太会推了,完美复现 NOI2021D2T2 被假 gcd 难住。
首先有一个结论:
一定不要循环走,待在原地等物品出现是最优的。
于是我们考虑时间倒流,假设终止时间是 \(0\),那么答案就是所有拿到物品的时间最大值,断环成链,假设以 \(i\) 为起点,时间就是 \(\max\{T_j + n - (j - i) - 1\}\),于是要求 \(\displaystyle \min_i\{\max_{i \le j}\{T_j + n - (j - i) - 1\}\}\),设 \(a_i = T_i - i\),那么就是维护 \(\displaystyle \min_i\{\max_{i \le j}\{a_j + i\}\} + n - 1\),然后我们考虑在 \(i\) 处计算贡献,同时我们发现,\(j\) 一定是一个后缀最大值,考虑线段树维护,那么每次 pushup
的时候,我们要知道当前位置的后缀 \(\max\),这个可以转移和楼房重建很像,于是可以类似地维护当最大值就在当前节点中,而不依赖外部传入的最大值时后的答案,每次 pushup 的复杂度就是 \(\mathcal O(\log n)\),总复杂度为 \(\mathcal O(n\log^2 n)\)。
代码
启示
- 千万不要弃疗,一定要注意有什么东西是在糊弄人的!
- 这种 \(\min\max\) 的式子可以想一想前缀 or 后缀 \(\max\),然后可以考虑楼房重建这种经典题(为什么考了几次这个技巧还是不会啊啊)
Luogu4426 [HNOI/AHOI2018]毒瘤
给出一张联通图(\(m \le n + 10\)),求独立集方案数。
\(n \le 10^5\)
动态规划
虚树
题解
突然有 NOI2021D1T3 的感觉。
整场考试最有体验感的题目,靠着暴力获得了 80 分的好成绩,虽然离正解只差一步了。
我们将非树边标号,显然,我们可以设 \(f_{x, 0 / 1, s}\) 表示以 \(x\) 为根的子树,\(x\) 是否选择,目前 \(s\) 集合中的边已经有节点被占用了,这样转移的时候可以枚举子集,复杂度 \(\mathcal O(n 3^{m - n + 1})\),空间复杂度 \(\mathcal O(n 2^{m - n+1})\),时空都爆炸。
然后我们将非树边涉及的点称为关键点,于是注意到,如果一个子树里面都没有关键点,那么是对于整个集合都没有影响的,于是没有必要 DP \(2^{m - n+1}\) 的值,于是我们可以保留关键点,建立虚树,每次转移的时候可以处理一下虚树上的边的系数,那么复杂度就降为 \(\mathcal O(2(m - n + 1) 3^{m - n + 1})\) 了。
好像别的题解直接枚举每条边的情况再 DP?但是我是直接保留的,其实没有太大区别,具体实现的时候,可以动态申请空间,又因为虚树只要求一遍,于是我们可以 \(\mathcal O(n)\) 地建立虚树以减少代码量。
代码
启示
- 当有用的点较少的时候一定要想起虚树!