JSOISC2023期间HZOI的模拟总结
前言
今年没去镇江扬中 JSOISC,留在学校打模拟赛。
话说 JSOI WC&SC 开始线上第一年的时候我参加了,一直到线上最后一年我都参加了。
然后今年疫情结束回到线下第一年,我又不参加了,完美避开所有线下 JSOI WC&SC。
其实我是很想去外地玩玩的 qaq
不能出去玩,只能在倒霉学校打膜你赛,然后被吊打。
一共五场,除了 sb 错误导致的挂分,就是不会的。总之问题巨多。
这篇主要是做题时候的想法和过程记录,以及一些反思和总结。
Day1(07-06)
总体情况
五场当中最良心的一场,水部分分给的还算比较多,题目总体不难。
并且没有倒霉 subtask(铺垫
60+20+60+60=200,rk2(不记得了,好像是这个
这一天不知道题目来源是什么,所以放不了题面。
T1
简单贪心,手推规律。
然而看到石子合并条件反射的区间 dp,但只能拿 \(20pts\),还卡了有一会儿。可这题要的是差,经典石子合并是和,虽然区间 dp 也能做但其实多此一举,所以不能总是条件反射。
后来推出结论了,忘了特判 \(n=1\) 的边界情况,在多测的情况下直接 \(100pts \rightarrow 60pts\),还好不是 subtask,不然可能直接寄成 \(0pts\)。
T2
被自己的经验主义坑了。
一眼看上去像扫描线,于是先打了一个离散化,然后觉得自己写线段树维护大概率错,并且线段树维护扫描线细节巨多,害怕爆零,其实只是懒,不想打线段树,就用 \(n^2\) 的朴素方法写了一遍。
一些有趣的逝:
有前几届聪明的学长,为了让自己的程序过,把此题的时限调成 20s,使我的 20pts 做法变成 60pts,还把机房上届学长的动态开点权值线段树放过去了(巨
后来 nhr 学长把时限调回 2s,我的代码被卡回 \(20pts\),那个动态开点权值线段树的代码也被卡成 \(20pts\) 了(乐
据说这题也能用扫描线+线段树写,但是常数巨大,有一个数据点专门卡线段树。
下面回归正题:
这题和扫描线的区别是:扫描线求的是面积并,而这题其实是求面积交。
众所周知,矩形的面积并不好求,因为并出来是一些奇奇怪怪的形状。但是矩形的面积交是矩形,可以很简单地线性求。
然而考试的时候以为面积交非常难求(?)也不知道怎么想的(脑子坏掉了真的是
这题由于是求 \(n-1\) 或 \(n\) 块地毯的交的并,可以先枚举去掉哪一块地毯,用前缀和和后缀和快速算出每 \(n-1\) 块地毯的面积交,然后要把这些面积并起来。
用总的减去重叠部分,不难发现重叠部分(即所有 \(n\) 块地毯的面积交)被多算了 \(n-1\) 次,减掉就行了。
nhr 学长提供的写代码的小技巧:定义一个求矩形面积并的运算。(非常好用,用了之后代码写起来非常方便而且容易理解。
这一个 T2 怎么写这么多
T3
KMP 求循环节,然后计算即可。
然而忘了 KMP 怎么写。(只能说我太弱了wssb
KMP 模板题的题解,写得都很好,没事多看看
主要还是得理解 KMP 的核心思想,它是如何减少冗余计算,快速求解的。
\(next\) 数组的定义:对于字符串的每一个前缀,其最长 \(border\) 的长度。(\(border\) 指对于一个字符串非自己本身的子串,满足这个子串既是原字符串的前缀,又是它的后缀)
真的废了怎么什么都记不住怎么什么都不会用
有同学用 \(O(n^3)\) 的暴力水过去了,还跑得飞快。膜。
T4
《关于数据水我的做法其实可以 \(100pts\) 然而我数组开小了挂成 \(60pts\) 这件逝》
《关于假做法跑得比正解还快这件逝》
我的想法是枚举 2 个主站点(由于主站点之间必须有边所以就是枚举每条边),然后分别枚举它们可达的点,取共同的可达点中最大和次大的值更新答案(这题非常友好它没有负数,所以两个数越大乘积就越大,可以放心的这么做)。
我的做法和题解的区别就在枚举共同可达点的地方。我是用一个 \(vis\) 数组,先枚举一个点与它相邻的点打上标记,再枚举另一个点相邻的,如果有标记就去更新最大和次大值。
我当时对这个做法的时间复杂度分析是 \(O(nm)\) 的,以为只能过 \(60%\) 部分分,没检查数组大小,觉得开小一点没事。
后来同学提醒,把数组开大之后直接过去,虽然跑得很慢。但比正解快
回想一下这个做法大概率是跑不到 \(O(nm)\) 的,具体复杂度多少我还不太会分析。但是可能冗余计算比正解少一些。
感觉如果出题人想卡的话可能是可以卡掉的(好像菊花图就可以卡到 \(O(n^2)\),没细想但好像是可以的),但良心出题人没卡。谢谢出题人。
正解做法:用 \(bitset\) 每个点存一个 n 位二进制数,计算共同的邻接点时候就是两个 \(bitset\) 与一下,这样的空间复杂度 \(\frac{n^2}{64}\),时间复杂度 \(\frac{nm}{64}\),理论上都是对的,也可以过。只是跑得没有我们的暴力快
原因是做与运算的时候有很多没用的 0,都计算了一遍,而枚举邻接的边不会枚举到这些 0,所以 \(bitset\) 虽然优化掉了 64 的常数,但是存在大量冗余计算,所以跑得慢。如果这个图边特别多点比较少的话,用 \(bitset\) 大概会比暴力快很多。
对于 \(bitset\) 我一直不怎么用,我印象中这个东西只是用来优化常数的……其实 \(bitset\) 真的是个好东西()
Day2(07-07)
总体情况
均分最低场,但不是最难场。
15+25+0+5=45,rk1(这种垃圾分居然rk1简直不可思议。
rating change:1500+100=1600
题目来自 USACO。
T1
其实很简单的一个题目,有人漏看条件没做出来,我不说是谁。
先离散化,离散化结果是一个每行每列都只有一个奶牛的网格。(类似八皇后地图)
容易发现统计可包围的不同子集的数目等价于统计最小矩形的数目。最小矩形即四边上都有奶牛的矩形,数最小矩形可以避免重复。
然后枚举矩形左右两边(边上必须有奶牛),考虑左右两边固定时,有多少不同的最小矩形。
记左右两边上奶牛纵坐标分别为 \(l,r (l \leq r)\)。
由于固定的左右边上的奶牛必须统计进去,所以能被有这样左右两边的矩形包围且满足这是最小矩形的奶牛纵坐标一定 \(\leq l\) 或 \(\geq r\) ,并且这些奶牛的横坐标也一定在左右两边的横坐标之间。
计算答案的时候加上 \([0,l]\) 之间和 \([r,\infty)\) 之间的奶牛数目的乘积。(此处区间是离散化之前的坐标)
具体实现的时候,我考场上用的有点像扫描线的线段树维护二维数点(脑子出问题了),其实这是静态的,离散化之后用二维前缀和维护就行了。
从这题得到的警示:要看题目条件!题面,数据范围,输入格式都得看!一些邪恶的出题人会把很重要的条件放在输入格式里!
T2
看到题目第一眼被吓到了,加上当时降智,T1没做出来,就直接懵了。
打了一个 \(25pts\) 无脑部分分,好像还打错了(是所有起点出发可能到达的位置数,我写的是从某一个起点出发可到达的位置最多,求最大位置),但数据水,居然放过去了。
后来讲题的时候才发现题目读错了,还好没有去想正解不然可能浪费时间还没有分。
一定要认真读题,警钟敲烂。
这题确实代码有点长,还有一点思维,但其实认真想的话也许不难想到。代码能不能写对就是另外一个故事了
这题比较麻烦的点就是机器人的复制操作。
由于机器人最快一秒复制一次,所以合法情况下,能扩展到的点通过移动初始机器人一定能达到。因此,我们尽量让本体到达一个点而不是让副本覆盖到这。
并且我们想让经过的点更多,所以尽量不要重复经过一个点,因为这样会让机器人更多的复制而限制原始机器人能到达的位置。
这个有一点点难理解,可以自己在脑子里面模拟一下。
综上,可以 bfs 求解。先求出对于每一个空地,离他最近的岩石距离为多少,即到达这个点时最多复制多少次。
然后再来一遍 bfs,求出所有初始机器人可以到达的点,顺便记录到达所需最小步数,求出复制次数。设初始机器人到达一个点最小步数为 \(step\),这个点到离它最近的岩石距离为 \(dis\),满足 \(\lfloor \frac{step}{D} \rfloor \leq dis\),那么初始机器人可以经过此点。(注意细节,题目中 \(D\) 秒后,所以等于号可以取)
然后再从初始机器人可达点向外扩展求出所有可以到达的点,这次搜索需要使用优先队列。称到达一个点还可以继续扩展的次数为扩展半径,每个点被扩展到的方式可能有多种,扩展半径最大一定是最优的,所以每次使用优先队列找到目前扩展半径最大的点来扩展。每个点最多被扩展到一次,优先队列保证了扩展到时最优。
前两次 bfs 时间复杂度都是 \(O(n^2)\),第三次 \(O(n^2 \log_2 n)\)。
T3
一个 dp。话说这五场所有的 dp 题一个都没做出来。我太弱了。
难在转化。可以把牛的大小和牛棚大小排个序,大小相同牛在前。
为了方便理解,把牛看成 (
,牛棚看成 )
,转化成一个括号匹配问题。
设 \(dp[i][j][1/0]\) 表示考虑了前 \(i\) 位,有 \(j\) 个待匹配的左括号,此时有/没有最左侧失配的左括号出现。
转移的柿子看代码或者这个题解,懒得打公式
T4
考场上打了一个暴力。
题目比较难,坑先放着,有空就填。
显然大抵是没空的
Day3(07-08)
总体情况
人类智慧场1
我不是人类,不存在人类智慧,所以寄。
100+0+0+0=100,rk2(话说我干什么呢就打一个 T1
良心的学长给我们加了倒霉 subtask,导致我 T3 不能乱搞骗分(恼
rating change:1600+3=1603
题目均来自 JOI。
T1
这题表面上看上去很麻烦,甚至我觉得 \(O(nq)\) 算法都难,后来用四五十行代码打了一个很麻烦的 \(O(nq)\) 模拟。
还发现了一个性质:初始时 \(n\) 个雪球把数轴分成了 \(n+1\) 个区间,从左到右记为 区间1,区间2,区间3,\(\dots\),区间 \(i(0 \leq i \leq n)\) 的雪左边一部分到第 \(i\) 个雪球上,右边一部分雪到第 \(i+1\) 个雪球上。
这个性质看起来很好,但是我调模拟调了一个多小时,不敢继续浪费时间在 T1 上了。(去年被 T1 坑了导致爆零的惨痛教训)
后来 nhr 让我们再看一看 T1 到 T3,提醒这都是人类智慧题,让我们认真想一想。
由于思考 T2T3T4 无果,又回来看 T1,再看那个性质。其实就是要找出区间 \(i\) 中的雪到雪球 \(i\),\(i+1\) 上的各有多少,换句话说,就是要找到那个分割点(可能是一个点,也可能是一个线段,如果结束之后还有雪剩余的话)。
记 \(x\) 为雪球 \(i\) 从时刻 0 到某时刻到达过的最右边的点,\(y\) 为雪球 \(i+1\) 从 0 到该时刻到达过的最左边的点。
显然如果某时刻 \(x \geq y\) ,此时这个区间中的雪完全被清空。只要二分找最后一个 \(x \leq y\) 的时刻,\(x,y\) 很容易预处理好然后 \(O(1)\) 求。
二分找到这个时刻后可以通过下一次的移动方向找到分割点。如果这个时刻为 \(q\),就说明到最后整个区间也未被清空,此时不需要再找分割点。
于是飞快地(并不)写了这题,去掉快读也就二三十行,比模拟还好写一点。
由于不确定写的对不对,在代码最后加了一行注释“不是人类,没有人类智慧”之后交上去了。(不过还好最后过了
T2
考场上直觉告诉我是 dp,于是放弃。
可是我连暴力都不会写。真是废呢
首先这个序列一定是若干段连续下降的子串,设 \(dp[i]\) 表示让前 \(i\) 个数符合这个条件所需的最少交换次数。
转移方程 \(dp[i]=\min\limits_{j=1}^{i-1} dp[j]+calc(j+1,i)\),\(calc(l,r)\) 表示将区间 \([l,r]\) 还原至连续下降序列所需的最少交换次数。
如何求 \(calc(l,r)\) :
- 对于 \(\geq l\) 且 \(\leq r\) 的数,计算顺序对个数。
- 对于 \(> r\) 的数,计算在他右边 \(\leq r\) 的数的个数。
- 对于 \(< l\) 的数,在前面已经考虑过了。
用两个树状数组搞一下,时间复杂度 \(O(n^2 \log_2 n)\)。
T3
比较显然的图论建模,然而我不会建。
考场上瞎搞了一个程序,能过一些数据点,然而由于 subtask 一分也没骗到。(非常恼
偷个懒,借用一下上上届学长 heq 的题解。
sto heq orz
T4
题面很长,看着就不简单,考场上连暴力都不会打。
咕咕咕
感觉越写越水
Day4 (07-09)
总体情况
人类智慧场2
100+30+10+0=140,rk1(存在并列)
依然是 subtask,不给骗分
rating change:1603+90=1693
题目均来自 USACO。
T1
在考场上乱搞搞出来的 \(100pts\)。
平时我求逆序对都是离散化,然后从后往前扫,遇到 \(a[i]\) 就在树状数组 \(a[i]\) 位置加一,查询的是这个数后面(即已经加到树状数组里的)有多少比它小的数。
但是这题这么搞好像不太好做。
让我们充分发扬人类智慧(但这题也不算非常难想就是了
换个思路,众所周知,逆序对指 \(i<j\) 且 \(a[i]>a[j]\) 的数对 \((i,j)\) 的个数。
所以树状数组不存数的值,而是存每个数出现的位置,查询比它大的数中有多少出现位置比它小,也可以求。
而这题正好可以这么做。
显然当头发最大长度 \(j=0\) 时(即光头(bushi),头发的不良度是 \(0\)。
\(j\) 每增加 1,头发的不良度增加的,就是比原来的 \(j\) 大的数中在等于 \(j\) 的数前面的个数。
设 \(ans[i]\) 表示 \(\geq i-1\) 的数中逆序对的个数,对 \(ans\) 数组做前缀和就是答案。
话说这个记录编号前 6 位是 114541(逃
T2
有一点二维偏序的感觉。
一开始想法是建图,可能相互作用的粒子之间连边,可以证明连通块的个数就是答案。
后来又用并查集写了一遍。
但只会 \(O(n^2)\),实在不会优化。(悲
看了题解之后感觉有一点人类智慧。
先是常规手段,对第一维排个序,然后记 \(mn_i\) 为排序后 \([1,i-1]\) 中 \(y_i\) 的最小值,\(mx_i\) 为 \([i,n]\) 中 \(y_i\) 的最大值。
若 \(mn_i > mx_i\) 则对于前面的 \(1 \leq j <i-1\),\(x_j \leq x_i\) 且 \(y_j > y_i\),所以 \([i,n]\) 中的粒子不会与前面任何粒子发生相互作用,从 \(i\) 开始是一个新的连通块,答案 \(+1\)。
T3
好奇怪的洒水器
考场上只会暴力 dfs。
后来也发现了是一条只向右或向下的分割线,在拐角处装洒水器,其他地方随便装不装。觉得或许是个 dp 罢,还逝不写了。
设 \(dp[i][j][0/1]\) 表示分割线结尾在格点 \((i,j)\) 处,最后一段分割线向右/向下。
用 nhr 的话说,想出状态,后面推式子就是体力活。可是对我来说推柿子难度极大
画图或许会更容易理解一点。
不想打式子了,也不想画图,继续偷懒,借用题解
。
T4
挖一个坑放在这。
如果要补的话 这篇题解 和 xuyixuan 的博客 对照着看。
Day 5(07-10)
总体情况
最难场,寄
76+0+0+5=81,rk1(存在并列)
据说前几届学长最高分 140+(然而我连 100 都没
还是 subtask,导致我 T1 挂一个点直接寄整个subtask,-24pts(恼
rating change:1693+88=1781
五场下来(第一场 unrated,所以其实是四场)居然没有掉分,目前全 HZOJ ranting 甚至排到第8
(主要是 HZOJ 玄学的总分相同按最后提交时间排,和玄学的计算 rating 方法,撞到运气了(两次并列我都交的比较早()
T1 & T4 来自 JOI,T2来自 USACO,T3 来自克罗地亚某比赛
T1
最短路题有很多都是最短路树和分层图这两个套路。
——nhr
这题也就用这些东西瞎搞一搞大概就搞出来了。
然而我还 WA 了一个点,寄了整个 subtask。
第一篇题解是分层图最短路,先把 \(S\) 到 \(T\) 的最短路上所有的边找出来,最后 \(U\) 到 \(V\) 的路径一定是先到最短路点上,顺着或逆着最短路走一遍出去,到终点。
分层图第一层和第四层都是原图,第二层是最短路图的正图,第三层是反图,每个点向第二三层的自己连单向 \(0\) 边,第二三层向第四层连单向 \(0\) 边,注意二三层之间不能连边,因为是走第二层或第三层。
关于考试时候那个代码为什么 WA ,我研究了1天半终于搞明白了。
我考试的时候写的思路,是找到所有最短路边,把一个方向的边权置为 \(0\),然后跑 \(U\) 到 \(V\) 和 \(V\) 到 \(U\) 的最短路。我代码有两个 bug:
-
我找最短路边的时候,是先找最短路点,然后找这些点之间的边,这显然是错的。但实际上最短路点的连边 \(\neq\) 最短路边。应该是枚举边,然后 起点到边的出点距离+边权+终点到边入点的距离=最短路长,这是找最短路边的正确做法。
-
把一个方向的边权都置为 \(0\),不能保证只会在一条最短路上走一段。有可能先在某条最短路上走一段,再通过一段边权较小的非最短路边走到另一条最短路上,然后到终点。得到的答案不一定合法。
所以修改了一下,用了3层的分层图,和题解差不多。区别是题解建了最短路图的正图和反图,求答案跑了一遍最短路,我只建了正图,要以 \(U\) 和 \(V\) 为起点各跑一次。
T2
启发式合并。
然而像我这种蒟蒻不仅不会启发式合并,暴力还写挂了。
首先每对被仰慕的奶牛向仰慕它的奶牛连一条边,若某点出度大于1,入队。
对于队里的每个点,合并它指向的所有点,若出现新的出度大于1的点,继续入队,合并。合并是通过并查集实现的。
合并到最后结果是一个每个点出度 \(\leq 1\) 的图,图中的每个点,在原图中是一个点集合,这个集合中的点颜色必须相同。
能这么做的原因,是因为颜色相同具有传递性,即三头奶牛 \(x,y,z\),若 \(x\) 与 \(y\) 颜色相同,\(y\) 与 \(z\) 颜色相同,则 \(x\) 与 \(z\) 颜色也相同。
然而!这么做是可以被卡成 \(O(n^2)\) 的!
所以需要用到启发式合并这种玄学操作。启发式合并,并不是什么高深的难以理解的东西,不过是普通的合并改变一下顺序,每次都把小的往大的上面合并。
神奇的是,这样做的时间复杂度是严格 \(O(n \log_2 n)\) 的!
考虑每个奶牛被合并的次数。若一个奶牛从一个大小为 \(x\) 的集合被合并到另一个大小为 \(y\) 的集合(\(x \leq y\)),合并过后集合大小为 \(x+y\),必然 \(\geq 2x\),所以一次合并大小至少乘二,合并到大小为 \(n\) 时最多被合并 \(\log_2 n\) 次。
真玄学
借鉴了这个题解,里面的图画的很形象,讲的也很具体。
然后这样做这题就变成 \(O(n \log_2 n)\) 的了,相比暴力代码甚至没什么要改的。(上面的暴力代码指的是正确的并查集合并代码,我这种写错的 sb 暴力不算
T3
树形 dp 题,考试时暴力不会打,订正的时候差点崩溃。
怎么会有树形的吊灯!你开灯不能一个一个开吗!为什么非要连着操作!你这样不浪费时间吗!!!属实逆天。
之后再次看了 heq 大佬的题解。
heq tql %%%
题解讲的非常详细了,图都画得很清楚。但像我这种废物仍然被一堆可能性和各种细节绕晕了,自己画了半天才大概搞懂。
像这种奇奇怪怪的状态,我在考场上是不可能想出来的啦。有这么多种情况的 dp,考场上根本调不出来的啦。这么多细节就算我写出来,肯定也是要挂好多分啦。
所以,综上,我不需要会这题(bushi
一些补充:
nhr 巨佬在讲题的时候提到了插头 dp,和这题有一些很相似的地方。
或许有空的话学一下,和这题类比。虽然不知道插头 dp 是什么,但是感觉这个是比较有思考价值的。
T4
据说是高维前缀和,但是总觉得很复杂。
坑先放这。不知道能不能填起来。(摆
后记
五天模拟结束了之后,被教练找去谈话。
他先让我具体分析了我每场比赛的情况,然后让我分析了一下自己的问题和后续如何继续提升。
我说我感觉自己做 dp 题完全做不起来,感觉写暴力可能分比写的 dp 高。
教练说 dp 之前确实是需要先写暴力的,在暴力的过程中分析需要记录的状态,写出来的 dp 也需要和暴力程序 d(ui) p(ai) 一下。
然而首先我经常连暴力都写不出来,其次写出来的暴力很有可能是错的,第三写完暴力我也设计不出状态,第四我并不是很会对拍,第五对拍出来 dp 是错的我不一定知道怎么调。
教练还推荐了一位巨佬 xuyixuan 的博客。
第一次写这么长的博客,但长的原因可能是把原本应该是五篇的合成一篇了。
花了不少时间,把订正的题目的解法比较完整地回顾了一遍,或许理解可以更深罢。
虽然 dp 题偷了一点懒但是有把整个过程所有情况再过一遍的()
又多了好多填不完的坑啊……
五天模拟结束,感觉到我的问题还是很多的,总之我很弱。我本来就是废物,也不知道怎么就走上了 oi 这条不归路()
感觉已经回不了头了,那就只能,继续走了。
最后真的非常感谢 nhr 学长这五天带着我们做题讲题,以及提出一些很有趣的问题。
那么,到此结束啦 qwq