NOIP2011题目简析
chunlvxiong的博客
DAY1:
T1:铺地毯
由于后面铺的地毯会覆盖前面的地毯,所以对于询问,你可以倒往上搜,一旦发现一张地毯能覆盖该点,输出其编号然后break。如果没有一张地毯能覆盖该点就输出-1。
T2:选择客栈
条件是两个(假设选择x,y两个客栈):
1、color[x]=color[y]
2、min{price[i]|x<=i<=y}<=p
首先你可以把客栈按照不同颜色分成若干类,并且是有序的(使用vector可以减少内存,P党请无视),这样第一个条件已经满足了。
接下来的操作都是对于同种颜色的客栈而言。
然后你发现,如果确定了开头,那么结尾越后,min值<=p的成功概率越大,所以你可以二分答案,确定第一个满足条件2的位置,这个位置以后的客栈也一定满足条件2,就可以直接累加。
确定开头+二分答案,复杂度已经达到NlogN,如果要验证是否满足条件2,可以使用线段树,复杂度是logN的,这样总复杂度O(NlogNlogN),约为65000000,经过检验,这样会T掉(得益于线段树的大常数)。
实际上你发现验证是否满足条件2是一个RMQ问题,那么ST表O(NlogN)的预处理和O(1)的询问可以使整个问题的时间复杂度降为O(NlogN),就可以A掉此题了。
看到这个题,发现整个图只有5*7的范围,n≤5实际上告诉你这道题是搜索。
那么,DFS还是BFS呢?这里考虑采用DFS,因为BFS空间比较耗。
朴素的DFS:
对于一个状态的每个方块,都穷举其左移或是右移的状态,得到下一状态。优先顺序按题目给出。
从朴素的DFS开始一步一步优化:
1、死循环问题:例如,我们先让x,y左移一格,没有方块消除,接着却又让x,y-1右移一格,局势回到了原来的样子,从而陷入死循环。
你当然可以在下一次DFS之前打个标记,告诉他是从这个方向过来的,如果没有方块消除,别走回头路,但是这个优化显然不够强力。
由于题目要求字典序最小,你发现x,y左移一格,与x,y-1右移一格效果是相同的(除非x,y-1上没有方块,不能进行操作)。
而后者的字典序比前者小,也就是说,只有左边格子上没有方块的格子才能进行左移操作,否则可以用右移代替。
2、什么样的情况被判定无解:因为存在横竖5个同时消除等很多复杂情况,不能单独从3消来考虑,但如果有一种颜色数目<3,那必定无法消除,判定无解。
3、此外,交换两个颜色相同的方块是没有意义的。
4、可以考虑Hash来避免到重复局面。(本人尝试了好几个素数做模数,也试了双重Hash,但是连样例也过不了……)
基本上,你依靠前3条优化DFS已经可以A掉此题了。
DAY2:
T1:计算系数
由于k较小,先花O(k^2)计算出杨辉三角。然后ans=杨辉三角对应项*(a^n)*(b^m),这里写了个快速幂,不过不写也没事。
T2:聪明的质监员
你很容易发现:区间的价值是随着w的减小而增大(或不变的),简单来说,Yi具有单调性。利用这个,你可以二分答案。
简单来说,就是对于二分答案出的w,计算出所有区间的总价值V,然后更新最小值。
若V>S,那么V增大没有意义,V需减小,也就是w增大。
若V<S,那么V减小没有意义,V需增大,也就是w减小。
所以问题变成了,如何快速计算总价值V。
不难发现,计算价值实际上分成两个独立的部分,一个是Σ1的部分,另一个是Σvj的部分。
考虑线段树,你发现它可以维护,但是对于不同的的w,每次需要重新建树,也就是说,用线段树每次计算总价值的O(NlogN)的,在乘以二分答案的log,以及线段树的大常数,这个算法要T。
实际上这个用前缀和思想维护就好了,这里没有min或者max之类的不可减的东西,你可以先分别计算Σ1和Σvj两部分的前缀和,到时候计算每个区间的价值,用(前缀和R)-(前缀和L-1)的值可以O(1)计算。
这样,你就可以在线性复杂度内计算总价值V,也就可以A掉本题了。
T3:观光公交
很容易想到贪心,但是怎样贪心才是正确的呢?
你先考虑一个氮气加速器都不用的情况,然后你要尽量使每个氮气加速器的价值充分发挥。
首先,氮气加速器的使用效果主要与两个因素有关:
1、人数
2、每站最晚的人到达时间(由于你必须等待最晚上车的那个人,所以氮气加速器使用过头了并不好)。
另外,你在一段路上使用氮气加速器,影响的人不一定是当前车上的人。
具体一点,假设在x站到x+1站之间的路上使用了氮气加速器,称y站为第一个车到达时间小于最晚上车的人到达时间的站(使用之后的效果),则以x+1站~y站为终点的人才能受益,原因在于,以y站以后为终点的人将会因为等待使此次氮气加速器的作用失效。
你肯定需要快速计算以x+1站~y站为终点的人的个数--前缀和可以解决这个问题。
使用一个氮气加速器是会影响后面氮气加速器的使用的。如果你在x站到x+1站之间的路上使用了氮气加速器,那么:
1、为了保证此次氮气加速器的效果始终有效,前面的站可能要减少使用氮气加速器的上限
2、后面的站会受到否则这次用的可能失效,而后面的位置则直接受这次使用的影响而减少了使用氮气加速器的上限。
所以简单地每次使当前氮气加速器发挥最大价值感觉上不对,然后……思路好像断了。
但是实际上,这是正确的--氮气加速器无论放在哪个位置,都会影响所有的位置,而且影响幅度都是一样的(使用氮气加速器的上限-1,除非本来已经不能使用了),所以你每次贪最大的就好了。
这样,你可以得到一个O(KN)的算法,也就是对于每个氮气加速器,你都贪心令其发挥最大价值(具体实现可以先对于每个x算出y,然后再取最大值,注意最后的更新),看上去要T。但是你可以发现一个小优化:如果一个氮气加速器最大贡献已经为0,后面的氮气加速器的最大贡献一定也为0,可以直接退出循环。于是,这个问题就解决了。