2017.07.11【NOIP提高组】模拟赛B组

时间:2022-04-23 14:01:07

Summary

  今天的比赛打得还不错,第一题被同桌灌输的贪心,纯模拟*了,然后steal的看了一下,发现怎么也对不了,一直在检查。最后10分钟才找出反例,推出动态规划方程,没有想到怎么转移,比赛就结束了。第二题题意理解错误了,但是还是拿到了充满希望的10分,第三题看到题目就直接上了线段树,我想没几个人能像我一样5分钟想完并打完这道题了。贪心一定要看到反例,不能盲目去做,否则浪费了时间,更让心情愈来愈不甘。

Problem

T1 解题

题目大意

  奶牛有P (P≤300) 道题目要做。他们的月薪是M (m≤1000) 元。
  每做一道题需要两笔付款,第一笔A_i(1≤A_i≤M)元在做题的那一个月初支付,第二笔B_i元(1≤B_i≤M)在做完后的下一个月初支付,牛没有任何存款意识
  题目必须按大概顺序解出。比如,题目3必须在解题目4之前或同一个月解出.

  找出牛们做完所有题目并支付完所有款项的最短月数。

想法

  刚开始比赛的是被被传销*成贪心,结果一直在检查错误,殊不知,其实是动态规划,下面给一个贪心的反例

  7 3 
  2 5 
  2 5 
  5 2

  按照贪心,答案是6,其实最优应该是5

  我们有两种DP的方法,一种是被姚大神称为垃圾的,另一种也是被他称为垃圾的

(1) 动态规划

  我们设f[i,j]表示你解答前i个问题,最后一次连续解答了j个问题的最少月份数

  状态转移方程:

  1.在前i~j件事情付清尾款当月付j个项目的首款:d[i][j]=min(d[i-j][x]+1) 当sumb[j-1-x+1][j-1]+suma[j][i]≤pay}

  2.在前i~j件事情付清尾款后的一个月付j个项目的首款:d[i][j]=min(d[i-j][x]+2) 当 sumb[j-1-x+1][j-1]≤pay && suma[j][i]≤pay

  x表示上一个月同时付了x个problem的首款,suma,sumb分别是输入的前缀和

  假设我们现在已经完成了i-j个problem的解答(上一个月同时付了x个problem的首款),现在要付连续j个problem的首款。在本月结束时,我们要保证前i个程序都已经在解答中(或者已经解出)且本月进行了j个解答,那么截止本月初付过前x个problem的尾款后,总共是完成了i-j个problem;本月要完成的问题的编号是从i-j+1一直到i,那么我们就可以推出,上一个月要完成的问题的编号为i-j-x+1i-j。按照我们上面分析的,我们有两种选择: 
  1.同时付清上一组x个problem的尾款和这一组j个problem的首付; 
  2.在上一组x个problem的尾款付清后,进行j个problem的首付。 
设suma[i][j]、sumb[i][j]分别为首付前缀和尾款前缀和,分别表示第i个问题至第j个问题的首付之和、第i个问题至第j个问题的尾款之和。 
  无论如何,都需要保证这个月的结余必须为正数、且下一个月将要付清的本月尾款必须也不能超过工薪(如果大于了工薪,那么本月或下一个月奶牛就破产了=_+)。

(2) 动态规划

  我们设f[i,j]表示你在第i天,选了j个任务的最少欠费。枚举一个k,表示上个月选到最后的题目是多少

  方程为f[k,i]:=min(sum_b[j+1,i])当sum_a[j+1,i]+f[k-1,j]≤m

  sum_a[j,i]表示a[j]到a[i]的和,sum_b亦然

  sum_a[j+1,i]+f[k-1,j]≤m其实就是如果上个月选的题目的首付,之前的欠费,是否会使奶牛破产

  f[k,i]表示,当前的欠费,上一次选了多少题目的第二笔付款,欠费就是对应的sum_b

T2 JIH的玩偶(tree)

题目大意

  这张网以玩具厂为总代理(根),构成一颗树。每个节点都代表一个客户,且每个节点都有重要度ai。JIH想将这些客户划成若干类别,当然同一类的客户重要度相差太大总是不妥。所以JIH决定先进行市场调研。JIH会选择两个客户X,从X向根走一共k个节点进行调查。调查的结果是这条路径上重要程度相差最大的两个客户的差值是多少。因为特殊需要,要求重要度大的客户必须在重要度小的客户后面(顺序为X到根,若序列为递减,则输出0,详情见样例)。

想法

  树上倍增,第一次做

  其实就是类似于树上求RMQ,跟RMQ思想差不多,维护类似的,设f/gmax/gmin/gans[i,k]表示从i到i的第2k个祖先中,对应的编号,最大值,最小值,最大值减最小值。

  其实就是在是查询难一点而已

  对于每个询问,先找到最大的k使得g[x,k]在终点或终点以下,

  Ans=max(ans,gu[x,k],gb[x,k]-last);

  last:=min(last,gs[x,k]);//最小值

  x:=g[x,k];//当前点

  如此递归求解,直到x为终点

  画一下图就知道了,其实很简单的,不必多说

T3 进化序列(evolve)

题目大意

  一个基因Ax 可以进化为序列中在它之后的基因Ay。这个进化的复杂度,等于Ax | Ax+1...| Ay的值,其中| 是二进制或运算。
  Abathur 认为复杂度小于M 的进化的被认为是温和的。它希望计算出温和的进化的对数。

想法

  维护一个最大的k,表示当前a[i~k]的数or起来是符合题目条件的,且保证k>i

  其贡献值为k-i

  因为or运算是不会变小的,所以k是线性的,关键是快速判断a[i~k]的数是否符合题目条件

  可以用线段树,树状数组,RMQ

  不会的可以去学。