您的位置:首页 > 其它

【20150904】NOIP模拟套题01 day1 题解 & 总结

2015-09-07 21:43 375 查看

前言

  这是套不久前刚做过的一套题。可能是由于之前习惯了做思维难度比较低的题吧,比赛开始后刚看完题我就懵了——除了T1有点感觉,T2、T3连暴力也不会做!结束之后大家讨论,我才发现自己实在太渣了:怎么这么简单的解法我都想不起来……再吐槽一句:这题目名的花样,我也是醉了。

  

T1_线段树什么的最讨厌了

  题目大意:给出一个线段树的make_tree程序,该段程序会将 [0,n][0,n] 的区间构成一棵空线段树。给定 l, r 和 lim ( 0≤l,r≤109; lim≤2∗1090 \leq l,r \leq 10^9;~lim\leq 2*10^9 ),询问最小的 n,满足 0≤n≤lim0 \leq n \leq lim 且 [0,n][0,n] 构成的线段树中含有 [l,r][l,r] 这一子区间。输入含有 T 组数据 ( 1≤T≤1001 \leq T \leq 100 ),保证 lr−l+1≤2000\frac{l}{r-l+1}\leq2000。

  make_tree程序大意为:传入参数为 [l,r][l,r]。若 l=rl=r 则终止过程,否则设 mid=⌊l+r2⌋mid=\lfloor\frac{l+r}{2}\rfloor,递归调用 maketree(l,mid), maketree(mid+1,r)。maketree(l,mid),~maketree(mid+1,r)。

  

  题目描述看起来很蛋疼,看懂了就能发现这道题其实很简单。

  题目给出来的是最终的子区间 [l,r][l,r],不妨考虑怎么从这个子区间倒退回 [0,n][0,n]。这样想思路就清晰起来——枚举它的父区间,并递归操作。如果设 len=r−l+1len=r-l+1,那么区间 [l,r][l,r] 的父区间有以下4种可能:[l−len,r], [l−len−1,r], [l,r+len], [l,r+len−1][l-len,r],~[l-len-1,r],~[l,r+len],~[l,r+len-1]  需要注意的是,递归的枚举顺序应该与上方列举的顺序一致,这能保证更快出解;还可以加两个小优化:如果当前已知的最小答案 ans≥rans\geq r,或 l−len<0l-len<0,那么再搜下去也不会得到更好的解,可以直接退出当前过程。记得注意下递归的边界问题就好了。

  最后来分析一下这种搜索的时间复杂度。注意到题目中给了一个特殊的条件:如果设 p=llenp=\frac{l}{len},则 p≤2000p\leq2000。有什么用呢?因为搜索每向下递归一层,当前区间的长度至少翻一倍,即 lenlen 至少变为原来的2倍,也就是 pp 至少变为原来的 12\frac{1}{2}。而当递归到 p<1p<1 时,必然有 l<lenl,一定会被优化卡掉。所以递归最多有 log2000≈11\log2000\approx11 层,因此总的时间复杂度为 ,O(411∗T),O(4^{11}*T) 即约为 O(4∗108)O(4*10^8),加上优化,足够通过此题。

  

T2_已经没有什么好害怕的了

  题目大意:定义空字符串为一个合法串,如果 A 和 B 都是合法串,则 (A) 和 AB 都为合法的串。现在给出一个长度为 n、只包含左右括号的字符串 ( 1≤n≤1061\leq n\leq 10^6 ),问经过【每一个位置】的合法串有多少个?输入含有 T 组数据 ( 1≤T≤101\leq T \leq 10 )。

  

  咋看这题很繁琐……对于这种需要处理嵌套内容的题目,一般的做法是用一个人工栈模拟递归处理。对于这道题,比较容易想到的做法是在“递归”过程中用线段树直接累加答案。但是这样做很有可能超时(为防止打脸,先声明不保证一定不能过)。

  再考虑一下对于每个位置的答案可以如何计算。首先可以确定,同层的括号对的答案相同,因为当前层的括号对并不影响上一层的括号对的答案。由此可以发现,同层的括号对的答案只跟当前层的括号对数量,以及其上层的括号对的答案有关。进一步思考可以发现:如果用 Link[i]Link[i] 表示第 i 个括号为左括号时对应的右括号的位置,L[i]L[i] 和 R[i]R[i] 表示当前位置所对应的括号对,在同一层中其左边、右边的括号对(包括自己)的数量,Fa[i]Fa[i] 为其父辈括号对的左括号的位置,则Ans[i]=Ans[Fa[i]]+L[i]∗R[Link[i]]Ans[i]=Ans[Fa[i]]+L[i]*R[Link[i]]  得到这条递推式就好办了,用人工栈计算出每个位置上公式需要的几个数值,然后直接套用公式就可以了。时间复杂度 O(Tn)O(Tn)。

  

T3_我才不是萝莉控呢

  题目大意:有一个 n∗nn*n 的网格图 ( 1≤n≤1051\leq n\leq10^5 ),其边界坐标为 (1,1)(1,1) 和 (n,n)(n,n)。有个人初始站在点 (n,1)(n,1) 上,设他当前处在 (x,y)(x,y),每次移动他可以选择向 (x−1,y+1)(x-1,y+1) 或 (x,⌈y2⌉)(x,\lceil\frac{y}{2}\rceil) 移动,但选择后者需要付出 B[x]B[x] 的代价。给出数组 B,求其从 (n,1)(n,1) 移动到 (1,11,1) 的最小代价。

  

  当时的比赛上,我周边的所有人都被这奇葩的题面难倒了。虽然使用DP骗个分倒是十分简单(许多人就是因为骗了这题的30分才不至于吃鸭蛋的),但仍然不禁令人好奇,难道这DP都有优化?

  然而题解让我们大吃一惊。因为题目的特殊设计,这题和“求哈弗曼树的点权和”是同一个意思!起初我还没想起来——这哈弗曼树是什么?等到上网一查,居然就是【合并果子】!也即只要把输入的 B 数组当成“合并果子”里面每堆果子的个数,套用原程序就可以了。

  

总结

  做这套题的时候我的心情是崩溃的——除了T1有得高分的机会外,其他的题目能骗分已经算不错了,由此可见题目难度相比我暑假做的题有一定幅度的提升。

  不得不提的是:我在敲完T1的暴力搜索之后,发现暴搜可能会搜完全部可能的状态,想到利用优先队列存储搜索状态的好方法。实践证明,这样做可以在不搜出全部的状态前找到答案,且时间复杂度“似乎”比暴力还快——于是我果断上交。然而结果大出我意料之外,居然超时了,只有30分!我再交我前面打完、用来对拍的暴搜程序上去,就这样满分了……欲哭无泪……

  从上面的经历中,我发现我的一个比较大的软肋——不怎么会分析时间复杂度。第二种做法之所以超时,是因为每次将新生成的状态加入单调队列,会产生log级别的额外时间复杂度。而如果我静下心来思考题目给出的特殊条件,从而分析出第一种做法的时间复杂度,这题的100分就稳拿了。可见在对拍数据比较难出,无法验证是否超时的时候,就要想方设法算出程序的时间复杂度,以及一切可能出现的常数。从这一点来说我算是收获不少。

  进击的NOIP模拟开始了……到底还有多少套题目呢(累)?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: