NOIP2016题目简析
chunlvxiong的博客
DAY1:
T1:玩具谜题
直接模拟即可,对于每次操作直接计算出下一位置,时间复杂度O(M)。
T2:天天爱跑步
一个简单的做法:你可以预处理计算出所有si和ti的LCA,然后对于每个观察员,判断它是否在si到ti的道路上,如果是计算出到达它的时间,如果与其观察时间相符,则其能观察人数+1。但时间复杂度O(NM)要T。
我们把s到t的路径分成两段:s到LCA,LCA到t。
先考虑s到LCA的那段路:
如果处于x位置的观察员能够观察到此人,需满足条件:dis[s]-dis[x]=w-->dis[s]=dis[x]+w
你可以开一个桶来快速统计:一旦遇到一个s点,那么把dis[s]所在位置+1,然后统计x点时,由于x点可以统计的范围仅限于它的所有子树(包括它自己),所以你可以把dfs所有子树之后的dis[x]+w位置的值与dfs之前的dis[x]+w位置的值求差来快速得到x点经过的人数。
遇到LCA的时候,你要把对应的s的dis[s]所在位置-1。
再考虑LCA到t的那段路:
如果处于x位置的观察员能够观察到此人,需满足条件:len(s,t)-(dis[t]-dis[x])=w-->dis[x]-w=dis[t]-len(s,t)
你可以类似上述进行开一个桶进行统计,但是需要注意,这里可能出现负数,需要将其转化为正数。
还有一点需要注意的,就是LCA这个点可能会被重复计算,最后需要减去。
这样统计的总复杂度为线性的,就可以AC了。
T3:换教室
首先你肯定需要预处理出教室之间的两两距离。v≤300,考虑floyd计算最短路。
然后考虑概率DP:
用dp[i][j][0]表示第i个时间段申请了j次,当天没申请的期望。
用dp[i][j][1]表示第i个时间段申请了j次,当天申请了的期望。
方程如下:
dp[i][j][0]=min{dp[i-1][j][0]+dis[c[i-1]][c[i]],
dp[i-1][j][1]+dis[c[i-1]][c[i]]*(1-k[i-1])+dis[d[i-1]][c[i]]*k[i-1]}
dp[i][j][1]=min{dp[i-1][j-1][0]+dis[c[i-1]][c[i]]*(1-k[i])+dis[c[i-1]][d[i]]*k[i],
dp[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i])+dis[c[i-1]][d[i]]*(1-k[i-1])*k[i]+dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i]}
初值:dp[1][0][0]=dp[1][1][1]=0
最终ans=min{dp[n][i][0],dp[n][i][1]|0≤i≤m}
DAY2:
T1:组合数问题
由于k的值是固定的,你可以先花O(2000^2)的复杂度进行预处理,计算出每个C(n,m)。
但是这样对于询问是不起帮助的,由于题目要求0 <= i <= n,0 <= j <= min(i,m),所以你很快想到前缀和,这样询问就变成O(1)的了。
T2:蚯蚓
最简单朴素的想法:记录下所有蚯蚓的长度,每次找最大值。
每次找最大值可以用堆维护,但是m高达7*10^6,会T。
所以你不得不寻找更优的算法。
可以发现这样一个事实:后切断的蚯蚓长度一定小于先切断的蚯蚓的两段的长度之和==后切段的蚯蚓的两段长度一定分别小于先切断的蚯蚓的两段长度。
理由很简单:本来就比你短,后来每次只增长q,而你每次增长2q,肯定一直比你短。
那么也就是说你可以开三个队列进行维护:第一个队列存排好序后的原序列,第二个队列存切成两段后的第一段的序列,第三个队列存切成两段后的第二段的序列,每次的最大值就是三个序列队头元素的最大值,令该元素出队,然后把新的元素直接加入第二、三个序列的末尾即可。
小转化:其它蚯蚓全部增长q==被切断的蚯蚓的两段长度分别减少q,这样减少了运算次数,需要使用原值时可以用数值+i*q来得到原长。
这样你可以得到一个线性复杂度的做法,就可以A掉此题。
T3:愤怒的小鸟
n≤18-->状压DP。
朴素的转移:由于一般情况下,一次可以至少打两只猪(特殊情况除外,例如两鸟的x值相同等),所以你对于每个状态,穷举打哪两只猪,然后在O(N)横扫,还可以打掉哪些猪,然后转移到新状态,时间复杂度O(2^N * N^3)=T
实际上你可以先O(N^3)预处理出打两只猪时还可以打掉哪些猪(存成一个状态),到时候对于每个状态,穷举打哪两只猪,然后直接|运算即可得到新状态。注意:不要忘了穷举只打一只猪的情况。时间复杂度O(2^N * N^2)。
由于有很多情况和状态是不需要转移的,所以这个复杂度是可以A掉此题的。