2022.8.11 模拟赛

T1 card

20pts

  我们发现两个相同的数字合并之后会使得中间的数字全部被消去,所以中间的数字就不能和两边的数字共同产生贡献。所以中间的数字就变成了一个规模减小的子问题了,于是我们考虑分治。

  对于一个区间 [ l , r ] [l, r] [l,r] 弄一个双指针枚举两个相同的数字的位置 i , j i, j i,j。然后把这个区间拆成三部分: [ l , i − 1 ] , [ i + 1 , j − 1 ] [l, i - 1], [i + 1, j - 1] [l,i1],[i+1,j1] [ j + 1 , r ] [j + 1, r] [j+1,r],然后分别递归下去计算贡献,最后再用 max ⁡ \max max 并起来就搞定了。

  时间复杂度: O ( n 4 ) O(n^4) O(n4)

100pts

  考虑区间 d p dp dp,我们发现,小的区间可以向大的区间进行转移,很容易得出转移方程:

i f ( a l = = a r ) f l , r = f l + 1 , r − 1 + a l f l , r = max ⁡ k = l r − 1 { f l , r , f l , k + f k + 1 , r } if(a_l == a_r) f_{l, r} = f_{l +1, r - 1} + a_l \\ f_{l, r} = \max_{k = l}^{r - 1} \{ f_{l, r}, f_{l, k} + f_{k + 1, r} \} if(al==ar)fl,r=fl+1,r1+alfl,r=k=lmaxr1{fl,r,fl,k+fk+1,r}

  然后 O ( n 3 ) O(n^3) O(n3) 转移即可。

T2 que

  首先判断是否满足:

  1. n m o d    k = 0 n \mod k = 0 nmodk=0
  2. n ≥ k 2 n \geq k^2 nk2
  3. a 1 = 0 ∧ a n = 0 a_1 = 0 \land a_n = 0 a1=0an=0
  4. c n t ( 0 ) ≥ 2 k cnt(0) \geq 2k cnt(0)2k

  如果不满足直接返回 f a l s e false false(其中 c n t ( 0 ) cnt(0) cnt(0) 表示数列中 0 0 0 的个数)。

10 pts

k = 2 k = 2 k=2 的情况,只需要判断会否满足 c n t ( 0 ) ≥ 4 cnt(0) \geq 4 cnt(0)4 就好了。

100pts

  我们考虑用正数前 k k k 0 0 0 k k k 个集合中最小的数(后面称为左端点),用倒数前 k k k 0 0 0 k k k 个集合中最大的数(后面称为右端点)。这里可以发现,除了做右端点的 0 0 0 在我们要处理的问题中是没有区别的,所以我们可以吧剩下的 0 0 0 都看成 1 1 1(因为除了端点其他的数都只能充当使集合数量合法的填充物)。然后记录 t e m p temp temp 为倒数第 k k k 0 0 0 的下标,然后开始处理。

  维护一个队列,里面维护每一个左端点所在的集合中 1 1 1 的数量还差多少达到模 k k k 意义下的 k − 2 k - 2 k2(也就是除了左右端点之外,还需要的 1 1 1 的数量),并且要求队列中数值依次递增,具体来说这样维护:

  1. 枚举 i ∈ [ 1 , t e m p − 1 ] i \in [1, temp - 1] i[1,temp1],也就是枚举前面的 k k k 个左端点,分两种情况:
  2. 如果 a i = 0 a_i = 0 ai=0 并且当前 0 0 0 是 “左端点”,那么 c n t 0 + + cnt_0++ cnt0++,再把 k − 2 k - 2 k2 加入队列,然后暴力地把这个点移动到所有差的值大于她的点之前(保证单增)。
  3. 如果 a i = 1 a_i = 1 ai=1,那么我们考虑贪心,将这个 1 1 1 分给离 k − 2 k - 2 k2 差的最少的那个点(这里钦定如果全都已经塞了 k − 2 k - 2 k2 个的话就塞给最后一个)。
  4. 枚举 i ∈ [ t e m p , n ] i \in [temp, n] i[temp,n],也就是枚举 k k k 个右端点,还是分两种情况:
  5. 如果 a i = 0 a_i = 0 ai=0 并且队列中队首还没有被塞满那就说明说明当前这个右端点不能和前面的任意一个左端点匹配,那么直接 r e t u r n      f a l s e return \;\; false returnfalse,如果当前的已经被塞满了的话就说明这个左端点已经匹配上了一个右端点,把她弹出队就好了。
  6. 如果 a i = 1 a_i = 1 ai=1,还是像之前那样,贪心的给里 k − 2 k - 2 k2 差的最少的那个点。
  7. 如果最后队列空了,那就说明所有点都匹配上了,那就 r e t u r n      t r u e return \;\; true returntrue

  这个做法的正确性证明我还没看懂,所以就先这样吧。

T3 perm

20pts

  显然可以 d p dp dp,记录 f i f_i fi 表示区间 [ l , r ] [l, r] [l,r] 中以 i i i 结尾的最长交错子序列的长度,记 p j p_j pj 表示 a j a_j aj 和它上次转移过来的位置的值的大小关系,假设 f j f_j fj 是从 f k f_k fk 转移而来,那么 p j = 1 p_j = 1 pj=1 表示 a j > a k a_j > a_k aj>ak,如果等于 0 0 0 则表示 a j < a k a_j < a_k aj<ak,那么转移方程就是:

f i = max ⁡ j = l i − 1 [ ( a j < a i ∧ p j = 0 ) ∨ ( a j > a i ∧ p j = 1 ) ] ( f j + 1 ) f_i = \max_{j = l}^{i - 1} [(a_j < a_i \land p_j = 0) \lor (a_j > a_i \land p_j = 1)](f_j + 1) fi=j=lmaxi1[(aj<aipj=0)(aj>aipj=1)](fj+1)

  这样一次就是 O ( n 2 ) O(n^2) O(n2) 的,总复杂度是 O ( q n 2 ) O(qn^2) O(qn2) 的。

100 pts

  对于一个数列,我们发现在一个 “波峰” 中最多贡献三个点,同理,每一个 “波谷” 也最多贡献三个点,所以我们考虑求出一个数列中所有的 “极值点”,这个 “极值点” 的定义就是她必须同时小于或者大于她旁边的两个值的点。

  求出 “极值点” 之后我们考虑一个区间中的最长交错子序列的长度就是区间中 “极值点” 的个数加上 2 2 2(这个 2 2 2) 就是区间的两个端点。

  于是我们要快速查询区间中拐点的数量,就考虑挂一个树状数组维护 “极值点”,然后区间查询前缀和就搞定了。

T4

10pts

  这里就是 l i l_i li 等于 1 1 1 的情况。这样每次输出的都是前缀和,那么因为我们每次加的数 x x x 是恒大于 0 0 0 的,所以不可能出现 i > j ∧ r i < r j i > j \land r_i < r_j i>jri<rj 的情况,也就是说 r i r_i ri 是单调不减的。

  那么问题就转化成了一个最长不下降子序列的问题,显然就直接 d p dp dp

f i = max ⁡ j = 1 i − 1 [ a j ≤ a i ] ( f j + 1 ) f_i = \max_{j = 1}^{i - 1} [a_j \leq a_i](f_j + 1) fi=j=1maxi1[ajai](fj+1)

  这个显然就是一个二位偏序,挂一个树状数组维护,每次在 a i a_i ai 上插入一个 f i f_i fi,然后区间查最大值就好了。

  时间复杂度 O ( n log ⁡ n ) O(n \log n) O(nlogn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值