NOIP2013题目简析
chunlvxiong的博客
DAY1:
T1:转圈游戏
可以发现答案为[x+m*(10^k)]%n。
瓶颈在于k最大10^9,用快速幂轻松解决。
T2:火柴排队
容易发现,当最小的ai对应最小的bi,第二小的ai对应第二小的bi,……时两列火柴之间距离最小。
然后你发现可以一个序列不动,只移动另一个序列(两个序列都移动的最少步数是一样的)。
然后你发现ai的大小没有用,只有排序后的编号才有用,b数组同理。
举个例子说明吧:
排序后的a数组编号:2 3 1 5 4
排序后的b数组编号:1 4 2 5 3
答案就是1 4 2 5 3-->2 3 1 5 4的代价。
把2 3 1 5 4视为1 2 3 4 5,那么1 4 2 5 3就是3 5 1 4 2。
也就是3 5 1 4 2-->1 2 3 4 5的代价。
由于1 2 3 4 5没有逆序对,那么答案就是3 5 1 4 2的逆序对数(因为你每次调整可以减少一个逆序对)。
T3:货车运输
首先容易发现开车经过的路多不是件好事情。
很多路实际上根本不会走。哪些路会保留呢?
答案:最大生成树(这题可能是森林)中的边才会保留。(这一步很机智)
道理很显然,如果你可以用大的边走到一个城市,为什么要走小的边呢?
所以你先kruskal跑一遍最大生成树,这样图就变成了一颗树(或是森林)。
如果询问的两个节点不在一棵树里,那么显然答案是-1。
否则你可以参考倍增LCA的做法,仍然用倍增思想,只不过维护的是min值。
这样这个问题就解决了。
DAY2:
T1:积木大赛
这个问题其实不难。
首先你发现一次操作的区间长度越长越好。
如果a[i]<a[i-1],那么当a[i-1]变为0的时候,a[i]也肯定为0了,不需要额外的操作步骤,否则需要多a[i]-a[i-1]步骤。
复杂度O(N)。
T2:花匠
我的做法实现起来很烦,但是思维难度几乎没有。
因为有两种不同的方式,你肯定要分开处理。
只考虑第一种,即2i-1<2i>2i+1的形式,另一种是同理的。
因为你发现实际上是一高一低的过去的,考虑dp,用dp[i][0]表示它作为高的那个最大的摆放数,dp[i][1]表示它作为矮的那个的最大的摆放数。
每个数dp[i][1]=1
dp[i][0]=max(dp[j][1])+1(需要满足h[j]<h[i])
dp[i][1]=max(dp[j][0])+1(需要满足h[j]>h[i])
朴素的转移是O(N^2)的,所以会T掉。那么你考虑开一颗线段树按照h值维护,然后你可以在O(NlogN)的时间内完成DP(其实树状数组也行)。
然后你就可以A掉此题,但代码长度2K以及时间都很不理想。
然而实际上你A完之后会很震惊--有比你短得多也快得多的O(N)做法。
首先仍然是两种情况分别处理,假设处理2i-1<2i>2i+1的形式。
那么你先假设h[1]是要留下的,然后你需要寻找比它大的数。
如果下一个数刚好比它大,那么就把它作为序列下一个数即可,之后你要寻找比它小的数了(同理进行)。
否则你就把当前这个数给改掉,改成这个比它小的数,以增加寻找比它大的数的可能性。
这样贪心去做,复杂度O(N),代码不到线段树的一半。
T3:华容道
这道题拿到题,看到n,m<=30,立即想到BFS。
由于有用的位置只有两个:目标棋子所在位置,空白格子所在位置,所以状态数最多n^4个,也就是一次BFS的复杂度。
由于q<=500,然后算了一下复杂度是O(4e8),期望卡卡常数过去,然后T掉了,尝试了好多次无果。
不得不思考更优的方案了。
可以发现空格移动次数太多而目标棋子移动的步数并不多,这个冗余可以大量的减掉。
很快想到,你可以用三维状态i,j,k表示目标棋子在i,j,棋子在其k位置(上下左右)来做(因为不然你目标棋子是动不了的)。
这样状态数变为n^2*4个,但是问题是你转移一步不是O(1)的了,应该是两次空格位置之间的距离+1(目标棋子与空格交换位置的一步)。
然后你会考虑去大力地跑bfs来计算空格之间距离(什么floyd,dijkstra,spfa),每个点都去跑一次,O(N^4)预处理出点对两两距离。
但是你发现这样是有漏洞的,因为你在移动空格的时候,可能一不小心根目标棋子交换了,导致这样的情况出现:空格到了指定位置,目标棋子跑到别的什么地方去了。
那么你就需要重新规划下预处理。
由于i到j的距离与j到i的距离其实是一样的,你可以对于每个i,j,k状态都跑一次bfs(注意,中途不能经过i,j点)。
这样你的预处理仍然是O(N^4)的,但是避免掉了原来的问题。
然后注意你待会不能再写bfs来转移三维状态i,j,k了,而是要写spfa……
然后你会写出一个2.5K左右的代码来A掉此题……