2016.07.19【初中部 NOIP提高组 】模拟赛C

题目:https://jzoj.net/senior/#contest/home/1766


T1:题目大意:在题目给出的m个字符串中找出其中每一个字符串在题目给出的另n个字符串所相对应的的一个字符串的前缀。


很明显对于第一问,求总共有多少个前缀,很明显就是二分,只要把n个字符串排个序就可以了(强烈谴责水过去的!)

对于第二问,我们只需找出在所有可能出现的方案当中的规律。

例:

f[0]=1,f[1]=1,f[2]=2,f[3]=3,f[4]=5,f[5]=8,f[6]=13……

明显这是一个斐波那契数列,高精度即可。


T2:题目大意:对于给定的m个区间[L..R],使得这m个区间里的R-L+1等于“一个相同的数”,并使得他们所到达这个数的绝对值的总和最小。


很明显这是找一个中位数,如果直接暴力求解的话,明显超时,只能水大概70分,时间复杂度是O(nm)。

然而很明显我们可以优化。

但,要明白优化什么。

其实,优化的就是我们如何确定n个数到中位数的距离和——亦指绝对值的总和。


例:

15 11 9 8 7 5 3 2

设中位数是8.

则,

大于8的有:15 11 9

小于8的有:7 5 3 2

计算方法为:15+11+9-(8+8+8)+(8+8+8+8)-(7+5+3+2)=26


很明显,我们可以把所选的数排好序之后分成两堆数,一堆是大于中位数的,一堆是小于中位数的。这两堆对应着不同的计算方法,而这计算方法简单易求,这里就不写了。

求的时候注意一个细节:

因为我们最终要优化的是时间,所以当我们在区间里暴力找中位数时,找到一半时即可停下,然后记录大堆或小堆。再用这一个区间的总和减去大堆或小堆则为另一堆的值。求总和预处理即可。


这样子效率就变成了O(Nlogn+nm/2+n²),勉强能过。



然而这分明是数据水的缘故。因题目的m<=200000,如果将m再改大一点,改成1000000或5000000,上述方法明显是过不了的,接下来要将一种O(n²+m)的算法。


这种算法其实与上述方法是差不多的,其中心思想就是找出一个中位数,然后用上述方法求出这个区间里的数与这个数的绝对值总和。

最大的不同其实就是找中位数的方法。

这里介绍一种数据结构叫做——堆。


{上述方法用了O(n/2)的效率找出大堆和小堆,这里的堆与真正的堆是不同的性质,我只是顺口罢了,也可以叫做大集合或小集合。但这种方法因题目限制,虽然速度很快,而每次区间求值的时候都有可能重复求值,这样子很明显效率不高。(但如果预先处理的话,虽然没能证实,但确实很难不用堆就做到O(n²))}


首先先要明白为什么用堆。因为我们转化后的核心思想是,预处理出每个区间的答案,然后对于m个区间直接输出这个区间的答案即可——亦即O(n²+m)。所以,我们在求这个区间的答案的时候就需要用到堆了。


我们设对于[L..R]这个区间的答案是f[l,r]则,如果我们要求f[l,r]的时候,我们需要维护一个最大堆和最小堆。


最终的就是维护的方法,其方法如下:

当前每次加入一个数,必须保证小根堆的个数大于大根堆的个数,亦即——第一次加入数的时候肯定是放到最小堆。

其次,我们还需保证小根堆里的每个值必须大于大根堆当中的最大值。

并且,我们当前大根堆的个数如果已经小于小根堆的话,则要把下一个数加到大根堆里,反之亦然。

而当我们加入一个数时,会有两种特殊情况:

设加入的数为x,如果欲加入到大根堆里时,x大于小根堆的最小值,则进行temp1操作。

反之————— 如果欲加入到小根堆里时,x小于大根堆的最大值,则进行temp2操作。

temp1:把x放到小根堆中,并且把小根堆的堆顶放到大根堆当中,并维护两堆性质。

temp2:把x放到大根堆中,并且把大根堆的堆顶放到小根堆当中,并维护两堆性质。


如果没出现这两种特殊情况,则把x放进其应该放进的堆中,并维护堆的性质。

这样做了之后,每次维护堆性质之后,小根堆的堆顶就是中位数,至于为什么,这里就不详细说明,可以自己草稿本上手模拟一下,然后你就会发现这是正确并且你举不出反例。


T3:题目大意:指在一颗树上第u个点到是否能到达第v个点,并输出到达的方案数以及距离。


gjx神犇讲述了一种叫做求时间戳的方法:

把图dfs一遍,求出从根到一个点的第一次访问时间,设为first[i],以及以这个点为根的子树的最大访问时间,设为finish[i],则满足v是u的子树的条件是first[u]=finish[v]。在满足这个条件的情况下,我们需要求距离。然而求距离的时候我们不可能直接暴力dfs,这样子,当数据坑你的时候,效率最坏O(n*m)。所以,我们可以预处理一下,在第一次dfs遍历图的时候就求出根到一个点的距离,然后u到v的距离就是s[v]-s[u].


T4:找出h个矩形,使得每个矩形上下贴合,且第i+1个矩形的宽度必须大于第i个矩形的宽度,并求出h个矩形的最大面积(有些位置不能摆,题目0的地方是可摆,1的地方不可摆,矩形必须在可摆的地方之中)。


很明显是动态规划,搜索只能拿到20分。

具体方法是:

设f[i,j,p,q]表示第i行,总共摆了j个矩形,第i行的下标为p,q的边为舞台底边的最大面积。

g[i,j,p,q]表示第i行,总共摆了j个矩形,第i行下表为p,q以内的所有可能存在的边为舞台底边的最大面积。

则显然:

g[i,j,p,q]=max(max(g[i,j,p,q-1],g[i,j,p+1,q]),f[i,j,p+1,q-1]);//至于这里为什么是f[i,j,p+1,q-1]请读者自行思考。

而当p~q区间里所有的数都是0时,则f[i,j,p,q]=max{f[i-1,j,p,q],g[i-1,j-1,p,q]}+q-p+1.


介于这种方法,还需要将行数滚动,才可AC。


现在主要是讲讲这道题动态规划的独特之处。

假设我们要求f[i,j,p,q]的最优解的时候,如果我们不知道p+1~q-1这区间的最优解,我们肯定要枚举当前p~q,看到底边以什么距离做下标能得到最优解,然而这样子毋庸置疑会时超。所以这里我们就加了一个g[i,j,p,q]表示p+1~q-1的最大值,则在求f[i,j,p,q]的时候就可以直接通过g[i-1,j-1,p,q]来当做其中的一个决策了,这样子就可以保证即可求出最优解,又能缩短时间,可以称为一大优化。其实就是借鉴了搜索的思想,在其上优化而已,虽没用dfs做过,但我觉得只要按着这种优化的方法和思想,也一定能用记忆化搜索做出。



总结:

估分 实际分

100  100

50   20

50   50

40   20

第2题就是因为忘记把longint改成int64,白白少了50分,而第4题则是高估了递归的效率。所以,总的来说考的还不是很好,第2题还是有失误,下次一定吸取教训,少犯错误。

你可能感兴趣的:(比赛题解,高精度,dfs,堆,DP)