历年NOI真题选做
没啥水平,大家将就着看,帮忙催更哦~
[NOI2013] 向量内积
观察发现暴力很足,可以直接 \(O(n^2d)\)。
继续观察数据范围,发现 \(K=2\) 或 \(K=3\) ,\(K=2\) 可以用 \(bitset\) 无脑草过去,复杂度 \(O(\frac{n^2d}{w})\)。
\(K=3\) 就魔改一下 \(bitset\)。考虑 \(\mod 3=0\) 的设为 \(0\) 其余为 \(1\) ,剩下的应该是 \(1\times 2,2\times 1,1\times 1,2 \times 2 ,\mod 3=2,2,1,1\),就相当于少算了 \(1\times 2,2\times 1\) ,于是按照 \(\mod 3==2\) 来补齐没算的。
直接枚举可能不太行,考虑随机打乱排列大概有 95 ,剩下的 5 分 reverse 一下排列就能得到。稍微判断一下(但是在考场上我可能只能搞到95)。
[NOI2016] 区间
类似于今年省选 A 卷 D1T1,就是不断往后加到合法,前面每次往后退一个。
[NOI2013] 快餐店
考虑一棵树,一定是最长链某个位置劈开。
现在是个基环树,考虑最长路径不经过环,那就把环劈了,找环上某个点,使得离这个点最远的子树距离最小。
这个可以把环拉开然后根据子树距离排一下,统计前后缀 max
计算答案。
但是可能最优位置不在环上,那我们还得把某两个点在环上的距离加上,就是计算的时候我们要算链长,环上距离长,子树深度。
统计的时候就是枚举环上某一个点 \(i\),更新 \(\min(\max(\) \(i\) 子树最大深度 \(+i\) 之前的链长 \(+i\) 之后的链长,\(\max(\) \(i\) 前面两个点 \(u,v\) 的最大深度 \(+(u,v)\) 在环上的距离,\(i\) 后面两个点 \(u,v\) 的最大深度 \(+(u,v)\) 在环上的距离 \()))\)
细节有点多。
[NOI2015] 寿司晚宴
怀疑我打过的这种套路的模拟赛都是从这题衍生来的。(你谷月赛,校内 NOIp 模拟赛)
考虑两个数不互质说明它们有公共的质因数,考虑 \(n \leq 30\) 一共只有 \(10\) 个质因数可选,于是可以设 \(f[s1][s2]\) 表示第一个数选的因数集合是 \(s1\) ,第二个数选的因数集合是 \(s2\) 。那么 \(f[s1|i][s2]+=f[s1][s2](i\&s2==0),f[s1][s2|i]+=f[s1][s2] (i\&s1==0)\) ,由于 dp
的时候数组值会改变所以要滚一下不能直接修改原数组(两个数组互相转移)。
然后发现可以按照 \(\leq \sqrt{n},\gt \sqrt{n}\) 来分类质因数,因为 \(\gt \sqrt{n}\) 的质因数最多只有一个。
那么就是 \(\leq 22\) 的质因数 (2,3,5,7,11,13,17,19) 和 \(\gt 22\) 的质因数。我们可以把每个数预处理一下,算出来它最大的质因数是多少,排个序,然后状压 dp
。
设 \(f[i][j][k][0/1/2]\) 表示前 \(i\) 个质因数中,第一个数选的小质数集合为 \(j\) ,第二个数选的小质数集合为 \(k\) ,两个人都没选 / 第 \(1/2\) 个人选了 第 \(i\) 个质因数的方案数。
转移:
发现 dp
的时候可以类似 \(01\) 背包那样从后往前枚举,这样可以省掉一维。
想的时候有点细节,写起来很舒适。
[NOI2018] 冒泡排序
发现 \(O(n^2)\) 有 \(80pts\) 的高分,就先去想了 \(O(n^2)\) 做法。
首先写一个暴力,打表长度为 \(n\) 以 \(i\) 开头的合法排列个数:
1
1 1
2 2 1
5 5 3 1
14 14 9 4 1
42 42 28 14 5 1
132 132 90 48 20 6 1
诶,觉得第一项好眼熟啊……萌神在我右边看了一眼,说:“不会吧不会吧不会真有人不会卡特兰数吧?”
卡特兰数是 \(\frac{\binom{2n}{n}}{n+1}\) ,于是我有了大胆猜想,把每一项都 \(\times (n+1)\) ,看到是这个东西:
1
2 2
6 6 3
20 20 12 4
70 70 45 20 5
252 252 168 84 30 6
924 924 630 336 140 42 7
3432 3432 2376 1320 600 216 56 8
12870 12870 9009 5148 2475 990 315 72 9
本来卡特兰数就有组合数,加上 \(20,45,252\) 这种具有提示性的东西,我就向组合数上想了……
大概过了半个多小时摸出来(结果萌神说可以用网格行走直接推)是:
嗯,不用写 \(O(n^2)\) 了,这个柿子可以预处理组合数 \(O(1)\) 计算。
考虑计算大于某排列字典序的排列个数,可以逐位计算大于它的排列个数,就是以大于它的数开头的排列个数,每一位依次算下去。
所以这个也是一样的道理,但是要扣掉一些不合法的,就是往后走的时候要把当前这个数减去比他小的数的个数,然后到某个位置不合法了就跳出,细节很多。
用网格行走推:
首先有个结论:最长下降子序列的长度不能超过 2。
证明可以看这个 ,感觉有点麻烦(我考场上肯定证不出来啊!)。
然后设 \(dp[i][j]=\) 选了 \(i\) 个数放入序列最大值为 \(j\) 的方案数, \(j\) 可以转移到比 \(j\) 大的,由于最长下降子序列长度不能超过 2 ,所以只能向未选的最小值选,那么 \(f[i][j] \rightarrow f[i+1][j+k] (j+k\leq n,k \geq 0)\) ,我们把 \((i,j)\) 抽象成一个点,实质上就是求 \((0,0) \rightarrow (n,n)\) 且不跨过 \(y=x\) 而且在它上面的,只能向上向右走的方案数(实际上这就是卡特兰数)。有了字典序的限制,可以考虑按照数位 dp 的方式一步步找限制,到某个地方超过限制了就跳出。
[NOI2019] 弹跳
\(1 \sim 8 :\) 暴力连边 \(+\) dij
\(9 \sim 13 :\) 离线预处理 \(+\) 暴力连边 \(+\) dij
\(14 \sim 18 :\) 线段树优化建图 \(+\) dij
随手写写拼拼就好了,所以我在考场大概只有暴力分……
考虑满分做法就是把一维的线段树优化建图搞到二维上去,直接二维线段树优化建图会被卡空间(卡得特别严重,吐了,不知道出题人什么心态啊),然后膜了膜题解(参考)
发现空间限制的瓶颈在于边比较多,由于 dij
的时候每个点都会只会更新一遍,考虑不直接连边,先把里层的建出来,然后对于外层的可以按照最短路的扩展方式,每次加边的时候就更新这一段的最短路,并且把更新过的区间加入队列中。这样就可以避免卡空间了,最大的点用了 123 MB 左右。(不带注释一共 4.73K,168行……)
[NOI2018] 归程
\(1\sim 4,6\sim 9,15,16:\) 有一个很显然的暴力,就是先预处理所有点到终点的最短路(建反图跑一遍),然后每次根据水位线建出车子可以走的图,做一遍 dij
,再枚举从哪个点下车是可行的而且路径最短,更新答案。时间复杂度 \(O(nQ)\) ,期望得分 \(50\) 。
\(5:\) 海拔只有一种,要么都能选要么都不能选,可以直接判断能不能走,\(ans=0\) 或 \(dist[n]\) ,期望得分 \(5\) 。
\(10,11:\) 考虑一条链的情况:存在某条边有积水,在这里必须下车,每次只要找到 \(1\sim n\) 的路径上第一条有积水的边就可以了,实际上可以预处理这条路径的前缀 min
,由于这个是单调不增的可以二分。由于链的包和暴力重合,所以链应该是对树有提示的。那么考虑树上某个点,我们可以走到这个点,然后从这个点下车走到终点,还是可以预处理终点到每个点的最短路,由于每个点到出发点的路径是唯一的,所以可以预处理路径最小值,每次找 \(\min \geq\) 水平面的且距离最短的,可以根据路径上的min
排序之后做后缀 max
解决。期望得分 \(10\) 。
\(12,13,14:\) 不强制在线可以把水位线排序,由于每次只会加边,所以可以先预处理最短路,每加一条边更新一下答案,就是加进来之后和 \(1\) 相连的连通块里的路径长度最小值。期望得分 \(15\) 。
所以比较正常的暴力大概有 \(80\) 。(写正解但是用了 spfa 的似乎没这个分……)
考虑正解:kruskal 重构树。树上某条 \(i \rightarrow j\) 的边表示图上 \(i\rightarrow j\) 的路径上边的最小值,这样根到任意一个点路径上的边权是递增的。由于根到 \(fa[i]\) 的距离比根到 \(i\) 的距离大,所以建树的时候用并查集维护,对于每条边 \((u,v,w)\) ,如果 \(v\) 到根的距离比 \(u\) 大说明 \(v\) 是 \(u\) 的祖先,那么就连一条 \(v \rightarrow u\) 的边。这棵树相当于并查集不路径压缩的状态,可以直接倍增跳,由于边权是降序的,所以跳到最后就是海拔高度。
[NOI2017] 游戏
上来看到这种东西: 若在第 \(i\) 场使用型号为 \(h_i\) 的车子,则第 \(j\) 场游戏要使用型号为 \(h_j\) 的车子。
这就是一个选了 \(i\) 就要选 \(j\) 的限制。
发现场地有 x a b c
四种,特别是这个 x
很麻烦,但看到有一半 \(d=0\) 的,所以先考虑 \(d=0\) 。
现在每种场地只能用 2 种车子,令 \(i\) 表示用第一种车,\(i'\) 表示用第二种车,分类讨论一下:
-
\(i\) 号场地不能用 \(h_i\) : 不用管这个条件
-
\(i\) 号场地可以用 \(h_i\) 但 \(j\) 号场地不能用 \(h_j\) : 连边 \(i \rightarrow i'\) 表示如果用了一定无解
-
\(i\) 号场地可以用 \(h_i\) , \(j\) 号场地可以用 \(h_j\) : 连边 \(i \rightarrow j , j' \rightarrow i'\) 表示如果用了 \(i\) 一定要用 \(j\) ,如果没用 \(j\) 一定不用 \(i\) 。
然后缩点,dfs,找 scc ,如果 \(i\) 和 \(i'\) 在同一个 scc 肯定无解,否则把缩点后的图拓扑排序,如果 \(i\) 的拓扑序在 \(i'\) 之后,说明 \(i\) 用第一种可用的,否则用第二种。这样可以获得 \(45\) 分的好成绩。
再考虑 x
,发现并不会 3-SAT
。
我们发现 x
非常少,只有 \(8\) 个。我们可以暴力枚举每个 x
适合哪两种赛车,这样是 \(3^d\) ,还有 \(20\) 分拿不到。
注意到我们枚举适合的赛车 AB AC BC
,就是 x=a / x=b / x=c
,其实我们已经尝试 “这个位置能否选 A B C
” ,所以可以任意去掉一种情况不要再尝试了。
这时候时间复杂度被降低到 \(O(2^d(n+m))\) ,可以通过本题。
数据太垃圾了,只有 #9 #10
有一丢丢强度,我清空写错了都有 \(90pts\) ……
[NOI2012] 骑行川藏
一开始以为全程一个速度,把式子展开一看觉得可能无解,重新看题发现每段速度不一样……
用数学语言描述这个题:
观察部分分发现会 \(n \leq 2\) 可以获得 \(40pts\) , \(n=1\) 的时候直接做,\(n=2\) 可以三分其中一个算另外一个。(我也不知道我哪里写挂了只有 \(35\))
考虑每一段都可以花费一些能量来减少一些时间,也就是每一段都有一个性价比。根据木桶效应,最优时应该每一段的性价比一样。我们可以二分这个性价比求解。
那么这个性价比怎么表示呢?当然是 \(\frac{\Delta t}{\Delta E}\) 这样的形式。这个就是导数的基本思想了。
为了方便我们把 \(v\) 作为变量,那么 \(F(v)\) 可以用 \(\frac{\frac{dt}{dv}}{\frac{dt}{dE}}\) 表示。
然后可以二分导数,然后二分出导数为 \(mid\) 时的最优答案。
在洛谷上一直 \(85\) 显示我输出 -nan
,卡了一个小时,去 loj 下数据发现没错,结果交到 loj 上过了??垃圾洛谷!
[NOI2017] 整数
暴力就是按题意来,直接模拟。
但是发现修改的位置连续性没有那么强,是一段一段修改的,可以用 set
维护一下最近的要改的位置,每次修改的复杂度就是 \(\log\) 的。
[NOI2019] 机器人
最基础的暴力有 20。
考虑稍微有点技术含量的,\(B_i \leq 10^4\) :设 \(F[l][r][x]=\) 现在的区间是 \([L,R]\) ,这个区间最高的柱子是 \(x\) 的方案数。我们枚举最大值所在的点 \(K\) ,那么 \(F[l][r][x]=\sum_K \sum_{y\leq x} F[l][K-1][y] \times F[K+1][r][y-1]\) 。
发现 \(300 \times 300 \times 10^4\) 会爆空间,但是仔细思考会发现很多状态是可以剪枝的,所以把每个区间 \([l,r]\) 编上号,冗余状态剪掉,只管有值的状态,这样可以获得 \(50pts\) 。
看到 \(A_i=1,B_i=10^9\) ,觉得应该有某种规律,应该是一个可以被插出来的多项式,那么正解也往多项式上想。
我们发现当 \(l=r\) 时,dp 状态是一个多项式 \(x^0\) ,前缀和是 \(x^1\) ,拓展合并后的多项式,发现 \([l,r]\) 的 dp 前缀和是 \(r-l+1\) 次多项式,然后插值。
考虑 APIO2016 划艇 的套路,把每个区间 \([a_i,b_i)\) 离散化下来,拆开,这样我们只需要 dp 每一段区间 \([L,L+n)\) ,对每个 dp 状态的前缀和进行插值。同时求出其在 \(L_{i+1}-1\) 处的值,再做下一段即可。
因为下标连续所以可以线性插值。时间复杂度 \(O(n^2m)\) 。
[NOI2014] 购票
考虑 dp ,\(f_i\) 表示从 \(i\) 出发,所需要的最小购票费用。
展开一下这个式子,移项:
考虑两个点 \(u,v\) ,假设 \(dis_u<dis_v\) ,那么 \(u\) 比 \(v\) 更优:
如果这个不是在树上而是在序列上,可以随便维护一个单调队列就没了。