和以前的总结一样,那些是人都会搞的水题就不写了囧……

【1】 Sept.29 「Poetize」杯NOIP模拟赛 VI
第一题(TYVJ P1962):
枚举及其优化。
设F[i][S]为买的方案为S(用10位二进制表示)时,能不能表示出整数i(1<=i<=100),这个显然可以预处理出来,然后将所有的状态S按照“第一关键字为买的个数递增,第二关键字为题目中的T递增“排序。枚举时,只需要按照排序后的顺序从前到后枚举状态,找到第一个能表示出所有给出的整数的状态即可。最坏情况下是10000*1024*10,会T,但题目中木有这样的数据囧……

第二题(TYVJ P1963):
DP,思想非常另类的那种……
每秒只有三种可能状态:不进行(0)、准备进行(1)、进行(2),且0的下一个状态是0或1,1的下一个是2,2的下一个是2或0,所以,假设时间是顺序的,设F[i][j][x]表示前i秒中状态1或2的有j秒,第i秒的状态是x的最大值……(剩下的不说了,注意第一个状态必须是0或1)
下面考虑时间是环形的情况。可以发现,只要每两个相邻状态都满足“0的下一个是0或1,1的下一个是2,2的下一个是2或0”,就是合法的,不过,最后一个状态也有下一个状态:第一个(因为时间环形)。
如果N=B,则所有的状态都可以为1或2,只需要找到U值最小的那一秒,将它的状态设为1,其它状态设为2即可;(特判掉)
如果N>B,则必然有状态为0,也就是可以从它开始。所以,时间仍然可以按照顺序的看待,只是注意若第一个状态是2,则最后一个状态不能是0——将第一个状态为0、1的与为2的分开处理即可。
注意,N=0与N=1时需要特判。

第三题(TYVJ P1964):
为了描述方便,可以将这个序列在格子中表示,如下图,表示序列(3, 4, 5, 4, 6, 3):
设最终的序列中每个数都为S。以下起第S行为界,上方的黑格需要消去,下方的白格需要补上。由于题目中只允许每次对连续的一段加1或减1,也就是消去同一行连续的一段黑格或补上同一行连续的一段白格,所以,容易证明,此时的最少操作次数就是(下起第S行上方各行的连续黑格段数之和+下起第S行下方各行的连续白格段数之和)。因此,问题就是枚举S并维护这个和值。
首先,预处理算出S=0的和值。此时只有黑格段没有白格段。虽然行数很多,但本质不同的行最多只有N个,所以可以在O(N)时间内求出(当然先要进行一个O(NlogN)的排序)。具体求法就不说了囧……
然后,考虑当S从0开始增加时,和值如何变化。显然,S从x增加到x+1,和值的增量是(第x行的白格段数-第(x+1)行的黑格段数)。由于每一行都是白段与黑段交替的,所以,若第x行与第(x+1)行本质相同,则和值增量只可能是1(第x行最左边和最右边的格子都是白的)或0(第x行最左边和最右边的格子一白一黑)或-1(第x行最左边和最右边的格子都是黑的)。第一个和最后一个格子的颜色只与最初序列中第一个和最后一个数有关,且必然下起一开始若干行都是-1,然后是0,再然后是1……也就是和值一开始不断减少,然后不变,然后不断增加,那么,不变阶段的和值就是最小值,其长度就是最小值的个数;
问题是如果第x行到第(x+1)行发生改变肿么办。此时,必然在原序列中有元素值为x,只需要把它们所在的位置的颜色从黑的改为白的即可。注意,在维护段数的时候,需要考虑到该格的相邻的格子的颜色,还需要注意边界。这样的操作有N次,所以,时间复杂度是O(N)的。
综上,可以求出在每一个本质不变的行段内的最小值及其个数,取最小的即可。总时间复杂度为O(NlogN)(最初的排序)。

【2】Oct.4 「Poetize」杯NOIP模拟赛 VII
第三题(TYVJ P1993):
注意到任意一个数能一步变换到达的数最多9*10+C(10, 2)=135个,只需要枚举一下,然后用二分查找找出这个数在不在(不能用Hash,会扎堆),若在就连一条边,然后求这个图的s-t最短路即可,需要使用Dijk_heap(SPFA估计会被卡,注意手写堆,不要priority_queue)。这样对于题目中的数据可以卡线通过(最坏情况下仍然会T,估计正解是某种设计比较好的Hash)。注意,使用DL边表时,需要在空间上作一些优化,否则可能MLE。

【3】Oct.20 Tyvj三周年邀请赛
第一题(TYVJ P2011):
压位即可。

第三题(TYVJ P2013):
首先转移方程是很好搞的……F[i]为前i个数的最大权值,则F[i]=max{F[i-1], F[j]+A[j+1]*B[i], 0<=j<=i-2},边界:F[0]=F[1]=0。问题是如何优化。
首先,由于A、B序列木有单调性,一般的斜率优化是不行的囧……
注意F[j]+A[j+1]*B[i],当F[j]求出来之后,这个其实是一个自变量为B[i]的一次函数,也就是一条直线(斜率A[j+1],纵截距F[j]),而且,由于B非负,所以这其实只是这条直线在y轴右侧的部分(射线!!)
本题需要维护的就是这些射线组成的下凸壳。注意,F数组是递增的,也就是插入的射线的纵截距递增。这样,插入射线y=A[j+1]*x+F[j](x>=0)时,(0, F[j])这个点要么在原来的凸壳上(如果大于上一个F[j]),要么在原来的凸壳内部。
如果在内部,则这条射线与原来的凸壳最多只有一个交点,因此只需要从左到右扫描原来的凸壳的边(这些边除了最右一条是射线外,其余都是线段),将一开始的在待插入射线下方的边都删去,直到找到一条边与该射线相交或者所有部分都删去为止。若某条边与待插入射线相交,则删去它在待插入射线下方的部分,再插入新射线,否则直接插入新射线;
如果在凸壳上,那么新射线有两种可能:一是斜率不大于上一条射线的斜率,此时该射线完全在凸壳外或凸壳上,直接舍弃;二是斜率大于上一条直线的斜率,此时,把原来凸壳上最左的那条边删掉,再按照内部的情况处理即可;
求F[i]时,找到x=B[i]与凸壳的交点即可;
显然,这些边可以用一个栈来维护(本沙茶一开始以为是队列,写完了以后才发现是栈……于是代码中按照队列的格式来写的囧……),每条射线最多进栈一次,出栈一次,加上二分查找的时间,总时间复杂度O(NlogN)。
写的时候注意一些细节,尤其要注意的是,算出F[i]后不能马上插入射线i,而要在F[i+1]算出后才能插入,因为j<=i-2!!(同样的,一开始也只能插入0,不能插入1)

代码:
P1962 P1963 P1964 P1993 P2013

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理