2019.12.21【NOIP提高组】模拟A 组总结

估分:100+0+10=110

实际得分:10+0+100=110

分数上估计的很准,但是题目位置上貌似有点小问题。。。

T1:

题目大意是让你求n的全排列中最长上升子序列个数是最多是k且包含给定的k个数的个数。

状压dp。

其中原排列合法的充要条件是: 

1.给出的子序列是原排列的子序列;

2.原排列的最长上升子序列长度为k。

可以设f[S]表示选数的状态为S时这些数所构成的序列满足条件的方案数。

一个条件是比较容易满足的,可以用n位二进制数表示每个数是否出现过。考虑从当前状态出发进行转移,枚举序列的下一个数 x,有以下两种情况: 若 x 不在给出的子序列中,则可以直接加入;若 x 在给出的子序列中,则要求其在子序列中的前一个数已经在当前状态中出现过。

第二个条件是本题的瓶颈所在,我们的状态必须要具有能力在枚举序列的下一个数 x 后求出以x 结尾的最长上升子序列的长度。 显然,如果把以当前出现过的每个数结尾的最长上升子序列长度都记录下来是无法承受的。而要想不记录以当前出现过的每个数结尾的最长上升子序列长度就能转移, 这就使我们联想到经典的 O(nlogn) 求长度为 n 的序列的最 长上升子序列的算法。

在这个算法中,我们需要实时维护长度为 i 的上升子序列结尾的最小值,这是一个单调增的序列,在这里称其为最小结尾序列。 那么,我们同样可以用 n 位二进制数表示每个数是否在最小结尾序列中,这样利用其单调增的性质就能恢复出最小结尾序列,从而在枚举序列的下一个数 x 后以 O(n) 的时间复杂度求出以 x 结尾的最长上升子序列的长度。

  由于在最小结尾序列中的数一定是已经出现过的数,我们把上述两个 n 位二进制数合并为一个 n 位三进制数即可。

对于一个三进制数,0表示没有选,1表示选了但不在最小结尾序列中,2表示选了且在最小结尾序列中。

在转移的时候,我们还是从小到大枚举x,尝试将x加入,在满足上述条件1的前提下,还需要满足条件2。值得注意的是原本经典做法中二分的部分我们直接变成从前往后枚举,并且x变大后枚举的位置并不用还原,直接继续往后枚举即可。

还有就是注意到当我们转移时新加入一个数时它一定在最小结尾序列中,同时有可能会踢出一个在最小结尾序列的数,这样就使一个原本是2的数位变成了1,从而可能转移到一个较小的状态,那么我们可以将1和2所代表的含义互换一下,这样转移时我们就直接将枚举的数所在数位上变成1(表示选了并且在最小序列中),然后将踢出最小序列的数所在数位变成2(选了但不在最小序列)。这样就能成功转移了。

时间复杂度 O(3^n*n ),空间复杂度 O(3^n )。

T2:

可以转化为网络流模型。

建立源点和汇点,每班第一人从源点连一条inf容量的边,每班每个同学向后一个同学连边,边权为前i个同学的负能量之和。每班最后一个人向汇点同样连这个班所有人的负能量之和。因为要求每个班必须选,因此不能割从源点到第一个人的边,除此之外,每割一条边表示选了这个班的前i个人。对于一个限制条件,班级v最多能比班级u少选w个人。也就是说割了班级u的第i点的边后,班级v的第i-w的点之前的边就都不能割,那么我们从u班的i这个点向v班i-w连一条inf的边即可,最后跑一边最小割就是答案了。

T3:

随便打。

先两个序列相加。

消去所有的大于1的数。

进位。

重复上述操作直至不能作上述任何操作。

A掉。。。

总结:这次比赛没有犯低级错误,但是分数可以继续提高。如第二题应想到是网络流模型,可多做一些这方面的题目。

但是第一题题目意思又没有理解,题目要求最长的上升子序列长度即为k,而我忽略了这个条件。根据这个条件就不能dg阶乘算了。而要用状压dp。

发布了199 篇原创文章 · 获赞 201 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/103674185