NOIP2007提高组题解

数据在这里(from oibh): http://www.cnblogs.com/Files/FancyMouse/NOIP2007.rar

1.count
一切直接nlogn的算法,在常数不大的时候都能过。我采用的是接近O(n+klogk)的算法,这里k=10000,即不同的数的个数:先把数存放到Hash表中,读完以后再取出Hash表内的数据排序。Hash函数采用mod 99991的方式。发生冲突采用类似开散列的解决办法:如果Hash(n)的位置已经被占用,但关键字不匹配,那接下来考虑Hash(n+1)的位置,直到找到一个空位,或者关键字相同。提一句:直至今年,C的stdlib.h库中qsort函数仍然可以使用。

2.expand
这题考查的是字符串处理的技巧。注意一定要 满足以下全部条件:非头非尾,两边均字母或均数字,左边字符比右边字符小。这时候才按要求进行填充工作。另:C/C++选手在熟练掌握for语句的情况下可以把顺序、逆序、大小写放在同一个过程里实现,降低编程复杂度。

3.game
注意到每一行都是独立的一个结构,因此可以把每一行的答案全部加起来就是最后答案。对于每一行,有状态转移方程:f(i,j) = 2*max(a(i)+f(i+1,j),a(j)+f(i,j-1)),边界f(i,i)=2*a(i),其中f(i,j)表示从i到j这一子段单独操作可以达到的最大权值。最后答案就把每一行的f(0,m-1)加起来即可。这里可以算得答案不超过30位10进制数,所以高精度的digit数组开到30足够了。乘法也不用写,用两次加法即可。

4.core
以下是我在比赛时的做法:
可以证明的是,如果图中存在多条路径,则在任何一条直径上都存在一条core(反证法,用到直径的距离最大性)。因此只需讨论一条直径上的core的情况即可。接着,如果一条路径包括了一条子路径的所有边,那么子路径的解不会比父路径更优。这一点后面将用到。
下面是算法:先寻找一条直径:任取一点u',遍历得到到它的最远点u,再对u寻找一个到它的最远点v,则路径u-v一定是一条直径(想想为什么),在刚才遍历u-v的时候记录遍历到每一个点的时候的前继,那么从v出发一直寻找前继到u,就能记录下这一条直径。这里复杂度是O(n)。
然后枚举core:注意刚才说到了路径是越长越好,因此枚举的时候,每枚举一个初始点,向后都尽量延伸到不超过s的距离。这样每个初始点只确定一个枚举对象。对于终止点的选定可以采用一个游标的方式,当初始点从u到v扫过一遍时,游标也从u扫到了v(回想怎么求解凸多边形最远点对的),因此这里复杂度是O(n)。
最后求ECC:暂时删除枚举对象(那条路径)上的所有边,然后以那条路径上的那些顶点作为源点开始遍历图,找到的最大距离就是该路径的ECC。这里复杂度也将是O(n)。
因此总复杂度为O(n)+O(n)*O(n) = O(n^2),ac。


赛后得出线性算法:
同样只要考虑一条直径。先对于直径上的顶点赋值:与该顶点连通的最远点的距离。这样可以构成一个线性模型,点(记为v[i])有一个值(记为f[i]),边有一个值(即原来图中v[i]和v[i+1]之间的距离,记为e[i])。这一步可以O(n)完成。
接着考虑这个线性模型。枚举过程还是和刚才一样。假设枚举的是v[a]到v[b]这一段路径,那么,这一段路径的ECC应当是以下几个值的最大值:1)max(v[a],v[a+1]...,v[b]); 2)max(v[i]+e[i]+e[i+1]+...+e[a-1],i<a); 3)max(e[b]+e[b+1]+...+v[i],i>b);
如果以上3个值能够在O(1)内回答,那么总复杂度就将是O(n)
对于1,显然这是个RMQ模型,有一个O(n)算法(当然st的O(nlogn)也可以接受)。或者考虑到这个问题的特殊性,所有(a,b)类的询问区间都不是严格包含的(即任取询问(a,b)(c,d)都不存在a>c&&b<d),单调队列也是可以采用的(而且常数小,推荐使用)
对于2,设一个数组a[1..n],a[k]表示max(v[i]+e[i]+...+e[k-1],i<k),那么a[k+1]=max(a[k],v[k])+e[k],因此整个a数组可以在O(n)时间内预处理得到,随后在枚举的时候直接查表即可。
3和2同理,不再详述

-------------------------------------------------
有建议请留言,谢谢~

转载于:https://www.cnblogs.com/FancyMouse/archive/2007/11/24/971375.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值