JOI Open 偷学记录

只偷了一题,被z宝吊打 /ll

  • 「JOI Open 2016」摩天大楼

把 a 排序,考虑 \(a_{i+1}-a_i\) 对于 \(\sum\) 的贡献,显然取决于一个 \(\le a_i\),另一个 \(\ge a_{i+1}\)\((f_i,f_{i+1})\) 的数量,也就是把前 \(i\) 小的数字插入数列,左/右尚未确定的数目,对这个 dp 就行了

但具体情况比较复杂,左/右是否确定的情况不同,而且端点(即在 \(n\) 个数插完之后位于 \(1\)\(n\) 的数)的某一边无论如何都无法计入贡献。我们只需要把左边未确定的看成左端点,右边未确定的看成右端点,对连续段进行 dp 即可。至于端点的特殊情况,我们再开一维来记录端点个数。

\(f_{i,j,k,d}\) 表示前 \(i\) 个点中,有 \(j\) 个连续段,和为 \(k\),端点数为 \(d\) 的方案数。答案即为 \(\sum f_{n,1,j,2}\),转移比较复杂,具体看代码

由于 \(n=1\) 的时候端点数只会有 \(1\),所以需要特判。

  • 「JOI Open 2016」销售基因链

考虑只有前缀怎么做,就是直接在 trie 树上搞,如果只有后缀的话就翻转当前缀做。由于 trie 树上的点的编号是连续的(排序后),于是这题就变成了一个二维数点问题。

具体一点的话就是,对于前缀的限制我们直接把字符串排序,并求出询问时前缀对应的区间,而后缀的限制就直接离线+trie查询即可

代码比较简短,冲上了 loj 次短解,而且常数巨小

  • 「JOI Open 2017」推土机

考虑给定平行线的斜率怎么做。可以画一条垂直于平行线的直线,通过点与直线做垂线的方式,把点一一对应到直线上。这样问题就转化成了求最大子段和。

可以证明最优解的平行线的斜率等于两点间的斜率,于是我们可以通过枚举两个点来枚举平行线。主要问题在于点在直线上的顺序会发生变化。

由于两个点之间的顺序只会变化一次,所以总变化次数是 \(\mathcal{O}(n^2)\)

考虑两个点之间的顺序什么时候会发生变化,当平行线的斜率和两点间斜率的大小关系产生变化时,两个点之间的顺序才会发生变化。

因此把平行线按极角排序即可。

代码,因为不会写所以只能贺z宝

  • 「JOI Open 2021」杂交

考虑 \(S_A=S_B=S_C\) 怎么做,也就是区间覆盖全局询问两个字符串是否相同,只能用一个数据结构去维护。

因此猜想,能杂交出的本质不同字符串的数量必然是有限的,而且是极少的,只要把这些杂交出的字符串一一用上述做法去做即可。

注意到本题杂交的运算方式,如果把 J 看成 0,把 O 看成 1,把 I 看成 2 的话,那么 \(A \oplus B = -(A+B) \mod 3\)

于是所有能求出的本质不同字符串都能用 \(aS_A+bS_b+cS_c\) 来表示,总共只有 27 种,打表发现字符串数量只有 9 种,直接暴力即可。 具体看代码

  • 「JOI Open 2021」决算报告

如果 \(D=n\) 显然是求 LIS,如果 \(D<n\) 就是一个有 \(l\) 限制的 LIS,我们只需求出每个 \(l_i\) 即可。

这题的限制换种说法就是,一个点 \(j\) 可以往点 \(i\) 转移,当且仅当 \([j+1,i-1]\)\(\ge a_i\) 的数组成的最长连续段长度 \(<D\)

我们维护的时候只需要搞出长为 \(D\) 的区间对后面点的 \(l_i\) 的贡献即可。只需一个线段树加单调队列。

求出 \(l_i\) 之后就可以直接一个树套树求 LIS ,时间复杂度 \(\mathcal{O}(n \log^2 n)\)

代码,喜提内存最大解。

然后发现按 \(a_i\) 排序就可以直接线段树,树套树是来白给的。

  • 「JOI Open 2019」三级跳

直接枚举 a/b/c 难以维护,因此猜测最优的 \((a,b)\) 数为 \(\mathcal{O}(n)\) 级别。

如果 \(i\)\(j\) 作为一组 \((a,b)\) 的话,\(i\)\(j\) 中间的所有数一定满足 \(a_k < \min \{a_i,a_j\}\),否则 \(k\) 可以取代 \(i\)\(j\),成为一组更优的 \((a,b)\)

我们枚举每个数成为 \(\min\) 的情况,往左/右找最近的 \(\ge a_i\) 的数,于是就找到了 \(\mathcal{O}(n)\) 组最优的 \((a,b)\)

至于询问我们可以直接离线,满足了 \(l\le a,b\) 后,对于 \(c\le r\) 的限制直接用数据结构维护即可。

具体看 代码,喜提次短解

  • 「JOI Open 2019」汇款

\(c_i\)\(i\)\(i+1\) 汇款的量,那么就有 \(n\) 个方程 \(a_i+c_{i-1}-2c_i=b_i\)

我们可以设 \(c_n\) 为未知量然后解一波方程就行了。但是有两个问题,就是方程里涉及的数字太大了,而且方程解出来会是负数或者浮点数,这咋办?

有个好方法是引入取模,把方程里涉及的所有操作全部对一个大质数取模就行了。由于解的范围在 2e9 左右,所以我设的模数是 mod=2999999929

至于为什么有些人设模数为 1e9+7 能过呢?小编也很好奇但是事实就是这样,如果有会证的观众朋友们欢迎来评论区婊一顿小编

还有一些情况要特判,比如说 \(a_i=b_i\)\(b_i=0\) 的情况。

具体实现看代码

posted on 2021-09-19 07:23  exzang  阅读(254)  评论(0编辑  收藏  举报

导航