为什么人机选的每道题都有 Ad-hoc 标签
\(\text{Ad-hoc}\) 专练
\(\text{BOI acronym}\)
首先,找出最左和最右的首个 \(B\) 是容易的,然后可以把两端非 \(B\) 的部分给扔掉,接下来的讨论默认 \(s_1=s_n=B\)。
定义在区间 \([l,r]\) 中,若某个字符的出现次数严格大于其余两种字符,则称该字符为区间 \([l,r]\) 的绝对众数。
观察到这道题如果是普通的众数,有很多东西都不好判断,因为会有两个字符出现次数同样多,但是如果是绝对众数,性质就很优秀了。
首先我们可以判断每个前后缀中,\(B\) 是否为绝对众数:若前缀 \([1,p]\) 中 \(B\) 为绝对众数,当且仅当 \(a_{1,p}=a_{2,p}+1\),后缀同理。
那么对于一个位置 \(i\),若 \(B\) 在 \([1,i]\) 为绝对众数,那么 \(s_i=B\) 当且仅当 \(a_{1,i}=a_{1,i-1}+1\)。
若 \(B\) 在 \([i+1,n]\) 为绝对众数同理。
那么目前判断不了的情况只剩下 \(B\) 在 \([1,i]\) 和 \([i+1,n]\) 都不是绝对众数的情况。
此时不难得出 \(I\) 和 \(O\)(或者 \(O\) 和 \(I\))一定分别是 \([1,i]\) 和 \([i+1,n]\) 这两个区间的众数。
进一步地,\(I\) 和 \(O\) 一定分别为 \([2,i]\) 和 \([i+1,n-1]\) 的绝对众数,那么不难判断 \(s_i\) 是否为 \(O\) 和 \(I\),若都不是,则 \(s_i=B\)。
砍树
将 \(1\) 定为根。
根据询问形式,我们可以把所有点按深度排序,然后逐个加入。
考虑当前已经加入的包含根的连通块为 \(S\),待加入的点是 \(i\),如何求出 \(i\) 的父亲编号。
对于已有连通块中的一条边 \((u,v)\),询问 \((i,u,v)\) 即可知道点 \(i\) 位于这条边的哪一侧。
由此可以考虑分治,进一步地,由于二叉树的优良性质,总能找到一条边,把 \(S\) 分成大小相差至多 \(1\) 的两部分。
每次加入点就跑一遍分治,单次分治的询问次数为 \(O(\log_2n)\),总询问次数为 \(O(n \log_2 n)\)。
但是会带有一些常数,需要注意的地方是,排序的比较函数尽量写得严格一些,否则 std::sort 可能会调用 \(30000 \sim 40000\) 次询问。
手写排序是最保险的。
\(\text{Life Lies in Movement}\)
生命在于孕动。
有一种做数学题的美感。
原题目中的式子非常不好处理,用惊人的注意力可以注意到下式。
那么把上式代入到原式的右边:
再注意到 \(f(x,u,v)-f(x,v,u)=dis(x,v)-dis(x,u)\)。
所以最终式子化为 \(\sum_{x=1}^n dis(x,v)-dis(x,u) \geq nk\)。
对于每一个点求出其他所有点到它的距离即可求解答案。
这一步使用换根 \(\text{DP}\) 即可,总复杂度 \(O(n \log_2 n)\)。
\(\text{An Array and Range Additions}\)
初步转化是若两个数 \(a_i,a_j\),覆盖它们的操作集合不同,则这两个数一定可以变为不同的值,也就是说,根据操作集合,可以把序列划分为若干等价类。
问题就变为,最少要进行多少次操作,使得不存在一个等价类中有颜色相同的数。
考虑进行 \(k\) 次操作,除空集外,最多能划分出多少个等价类。
由于每次操作会新增两个端点,一共会将序列分成 \(2k-1\) 段,容易构造出一种方案使得每一段都是不同的等价类,即最多能划分出 \(2k-1\) 个等价类。
接下来考虑到那些操作集合为空的数,那整个序列可能会形如:空——非空——空——非空……
需要保证为空的部分不能有颜色相同的数,\(\text{DP}\) 等想法至少都需要指数级的复杂度。
注意到若有两次操作 \([l_1,r_1],[l_2,r_2]\),满足 \(r_1<l_2\),即两次操作不交,那么可以等价地转化成 \([l_1,l_2],[r_1,r_2]\) 两次操作。
换句话说就是一定存在最优方案使得不进行任何操作的数是一段前缀和后缀。
那么可以枚举一段区间,钦定其中每个数都要操作,然后计算最少分成多少段才能使得每一段都没有相同的颜色。
枚举合法的区间可以用双指针优化,最少分成多少段可以用倍增优化。
总复杂度不难做到 \(O(n \log_2 n)\)。

浙公网安备 33010602011771号