版权声明:都是TATQAQ2333大爷教我的 https://blog.csdn.net/u012076197/article/details/53366110
NOIP 2016 Day2 解题报告
by 毒液哥
Problem
100Points
- 注意到n, m <= 2000. 而我们的问题是对范围内所有组合数统计能被k整除的个数,所以我们需要得到范围内所有的组合数模k的余数。
- 要注意j <= i,不要计算多余的0.
- 用杨辉三角形递推求出组合数,再用前缀和处理即可。
Earthworm
35Points
- 每次用线性时间找到最大值。
- 切成两条。
- 把其他所有的加上q.
- 记得输出。
65Points
- 35分算法中有两步操作使用了线性时间,其中操作1可以轻松用数据结构(堆,线段树,平衡树等)在对数时间内解决。
- 切成两条。
- 将原有的点长度都加q, 等价于将所有点的长度加q, 再将新点的长度减q. 于是第i次将新点减q(注意此时堆中的数都是已经减去 (i - 1) * q的, 所以新点进堆时还要多减去 (i - 1) * q. 换言之,堆中的数都是实际值减去 (i - 1) * q,因为我们每回合都将所有数加上q而并没有直接改变堆中的数)。
- 记得输出。
60Points(q = 0)
- 所有点都不会变大,而我们每次都会选最大点,则:每次选的点都不比上次选的点大。
切成两条由于我们每次都按比例将选的点切开,可以得到两个单调不减的序列。- 如果在一开始将原序列排序,那么我们每次都只用在这三个队列的队首中选择最大值。
- 记得输出。
100Points
- 除了新点以外所有点每回合增大q,则:每次选的点至多比上次选的点大q.
切成两条由于每次选的点至多比上次选的点大q,那么每次切成的两条相比上次切的对应的两条也至多大q,而上次切下的两条已经在该回合增大了q。因此我们仍然可以得到两个单调不减的序列。- 像65分算法一样处理q.
- 如果在一开始将原序列排序,那么我们每次都只用在这三个队列的队首中选择最大值。
- 记得输出。
吐槽
- 100分做法和60分做法几乎一毛一样。
- 这样的
坑爹数据排列方式我是头一次见到。 - 这题我挂了。
Angrybirds
Bruteforce
- 不知道有多少分。
- 枚举点对,求出抛物线方程。注意两点的x坐标不能相等,抛物线二次项系数必须小于0.
- 删掉在抛物线上的点。
- 进入下一层继续枚举。
60Points
- 如果使用暴力,我们会删掉一些线段然后进入下一状态。
- 一个状态是指现在平面内还剩多少个点。
- 注意到,从当前状态无论以什么方式删点,均不会影响之前状态到当前状态的决策。换言之,在之前的状态时,我们只需考虑:怎样删点来到达当前状态即可,而不用管当前状态下如何删点到达下一个状态。
- 上述性质即无后效性。
- 我们用一个二进制数S来表示一个状态,若S的第i位为1,则表示第(i + 1)个点还存在于平面内。这种表示状态的方式叫做状态压缩。
- 状压DP即状态压缩——动态规划的简称。
- 同样对于每个状态枚举抛物线即可。
100Points
- 平面内有n个点,共2 ^ n个状态。
- 每次枚举抛物线还要检查每个点,需要O(n ^ 3)的时间。
- 不如我们先枚举抛物线,预处理出每条抛物线经过的点。
- 我们用二进制数T来表示一个点集,若T的第i位为1,则表示第(i + 1)个点能被抛物线经过。用T & S即可得到当前状态该抛物线经过的点。
100Points
- 平面内有n个点,共2 ^ n个状态。
- 每次枚举抛物线还要检查每个点,需要O(n ^ 3)的时间。
- 每次都要枚举抛物线经过的两个点,即抛物线必定会删去的两个点。既然我们的最终目标是将当前状态所有的点都删去,那么可以知道:删掉所有点的最优方案中一定会有一条抛物线经过当前状态第一个点(否则就没法删掉它了)。
- 那么我们只需要枚举经过当前状态第一个点的抛物线即可(因为这么做一定不会做出错误的决策)。
100+Points
- 平面内有n个点,共2 ^ n个状态。
- 每次枚举抛物线还要检查每个点,需要O(n ^ 3)的时间。
- 那么我们只需要枚举经过当前状态第一个点的抛物线同时预处理出每条抛物线经过的点即可。
吐槽
- 第三题比第二题简单。
- 出题人为啥不卡100分算法呢。
- NOIP中涉及精度问题对初学者很不友好。
总结
- 总体难度比去年大了一些,但不会太多,但分比去年难拿太多。
- 数据档太多(或者说每个点都不一样)。
- 题目本身都有很好的Idea.
- 难题是检验心态的唯一标准。